diff --git a/backend/__pycache__/sensors.cpython-36.pyc b/backend/__pycache__/sensors.cpython-36.pyc new file mode 100644 index 0000000..9cc6e42 Binary files /dev/null and b/backend/__pycache__/sensors.cpython-36.pyc differ diff --git a/backend/sensors.py b/backend/sensors.py index 222d358..8d66e05 100644 --- a/backend/sensors.py +++ b/backend/sensors.py @@ -6,20 +6,84 @@ import serial as ser import pandas as pd from threading import Timer from sys import exit as ex +import time class Spectrometer: - def schedule(self, delay): - self.timerObject=Timer(delay, retrieveData) + def initializeSensor(self): + '''confirm the sensor is responding and proceed with spectrometer initialization''' + try: + rstring='undefined' # just need it set to a value + self.serialObject.write(b'AT\n') + rstring=self.serialObject.readline().decode() + if rstring == 'undefined': + raise Exception #sensor didn't respond + if rstring == 'OK': + pass #handshake passed + if rstring == 'ERROR': + raise Exception #sensor is in error state + except: + print('An exception ocurred when performing spectrometer handshake') + ex(1) + self.setParameters() - def __init__(self, path='/dev/ttyAMA0', baudrate=115200, timeout=1, refreshrate=1): + def setParameters(self, parameters={}): + '''applies the parameters like LED light and gain to the spectrometer''' + # try: + if 'it_time' in parameters: + it_time = int(parameters['it_time']) + if it_time <=0 : + it_time = 1 + self.serialObject.write('ATINTTIME={}\n'.format(string(it_time)).encode()) + self.serialObject.readline() + + if 'gain' in parameters: + gain = int(parameters['gain']) + if gain < 0 or gain > 3: + gain = 1 + self.serialObject.write('ATGAIN={}\n'.format(gain).encode()) + self.serialObject.readline() + + if 'led' in parameters: + led = bool(parameters['led']) + if led: + led=1 + else: + led=0 + self.serialObject.write('ATLED3={}\n'.format(led).encode()) + self.serialObject.readline() + # except: + # print('An exception occured during spectrometer initialization') + # ex(1) + + def startDataCollection(self): + try: + self.serialObject.write(b'ATCDATA\n') + rawresp = self.serialObject.readline().decode() + except: + print('An exception occurred when polling for spectrometer data') + ex(1) + else: + responseorder = [i for i in 'RSTUVWGHIJKLABCDEF'] + realorder = [i for i in 'ABCDEFGHRISJTUVWKL'] + response = pd.Series([float(i)/35.0 for i in rawresp[:-3].split(',')], index=responseorder) + self.data = pd.DataFrame(response, index=realorder, columns = ['uW/cm^2']) + self.schedule(self.rrate) + + def schedule(self, refresh): + self.timerObject = Timer(refresh, self.startDataCollection) + self.timerObject.start() + + + def __init__(self, path='/dev/ttyUSB0', baudrate=115200, tout=1, rrate=1, params={}): self.path=path self.baudrate=baudrate self.timeout=1 + self.rrate=rrate try: - self.serialObject = ser.Serial(path, baudrate, timeout) + self.serialObject = ser.Serial(path, baudrate, timeout=tout) except: print('An exception occured when opening the serial port at {}'.format(path)) ex(1) else: - initialiseSensor() - startDataCollection() + self.initializeSensor() + self.startDataCollection() diff --git a/backend/venv/bin/__pycache__/miniterm.cpython-36.pyc b/backend/venv/bin/__pycache__/miniterm.cpython-36.pyc new file mode 100644 index 0000000..15c52d2 Binary files /dev/null and b/backend/venv/bin/__pycache__/miniterm.cpython-36.pyc differ diff --git a/backend/venv/bin/miniterm.py b/backend/venv/bin/miniterm.py new file mode 100755 index 0000000..9731c3d --- /dev/null +++ b/backend/venv/bin/miniterm.py @@ -0,0 +1,976 @@ +#!/root/projekti/TeraHz/backend/venv/bin/python3 +# +# Very simple serial terminal +# +# This file is part of pySerial. https://github.com/pyserial/pyserial +# (C)2002-2015 Chris Liechti +# +# SPDX-License-Identifier: BSD-3-Clause + +import codecs +import os +import sys +import threading + +import serial +from serial.tools.list_ports import comports +from serial.tools import hexlify_codec + +# pylint: disable=wrong-import-order,wrong-import-position + +codecs.register(lambda c: hexlify_codec.getregentry() if c == 'hexlify' else None) + +try: + raw_input +except NameError: + # pylint: disable=redefined-builtin,invalid-name + raw_input = input # in python3 it's "raw" + unichr = chr + + +def key_description(character): + """generate a readable description for a key""" + ascii_code = ord(character) + if ascii_code < 32: + return 'Ctrl+{:c}'.format(ord('@') + ascii_code) + else: + return repr(character) + + +# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +class ConsoleBase(object): + """OS abstraction for console (input/output codec, no echo)""" + + def __init__(self): + if sys.version_info >= (3, 0): + self.byte_output = sys.stdout.buffer + else: + self.byte_output = sys.stdout + self.output = sys.stdout + + def setup(self): + """Set console to read single characters, no echo""" + + def cleanup(self): + """Restore default console settings""" + + def getkey(self): + """Read a single key from the console""" + return None + + def write_bytes(self, byte_string): + """Write bytes (already encoded)""" + self.byte_output.write(byte_string) + self.byte_output.flush() + + def write(self, text): + """Write string""" + self.output.write(text) + self.output.flush() + + def cancel(self): + """Cancel getkey operation""" + + # - - - - - - - - - - - - - - - - - - - - - - - - + # context manager: + # switch terminal temporary to normal mode (e.g. to get user input) + + def __enter__(self): + self.cleanup() + return self + + def __exit__(self, *args, **kwargs): + self.setup() + + +if os.name == 'nt': # noqa + import msvcrt + import ctypes + + class Out(object): + """file-like wrapper that uses os.write""" + + def __init__(self, fd): + self.fd = fd + + def flush(self): + pass + + def write(self, s): + os.write(self.fd, s) + + class Console(ConsoleBase): + def __init__(self): + super(Console, self).__init__() + self._saved_ocp = ctypes.windll.kernel32.GetConsoleOutputCP() + self._saved_icp = ctypes.windll.kernel32.GetConsoleCP() + ctypes.windll.kernel32.SetConsoleOutputCP(65001) + ctypes.windll.kernel32.SetConsoleCP(65001) + self.output = codecs.getwriter('UTF-8')(Out(sys.stdout.fileno()), 'replace') + # the change of the code page is not propagated to Python, manually fix it + sys.stderr = codecs.getwriter('UTF-8')(Out(sys.stderr.fileno()), 'replace') + sys.stdout = self.output + self.output.encoding = 'UTF-8' # needed for input + + def __del__(self): + ctypes.windll.kernel32.SetConsoleOutputCP(self._saved_ocp) + ctypes.windll.kernel32.SetConsoleCP(self._saved_icp) + + def getkey(self): + while True: + z = msvcrt.getwch() + if z == unichr(13): + return unichr(10) + elif z in (unichr(0), unichr(0x0e)): # functions keys, ignore + msvcrt.getwch() + else: + return z + + def cancel(self): + # CancelIo, CancelSynchronousIo do not seem to work when using + # getwch, so instead, send a key to the window with the console + hwnd = ctypes.windll.kernel32.GetConsoleWindow() + ctypes.windll.user32.PostMessageA(hwnd, 0x100, 0x0d, 0) + +elif os.name == 'posix': + import atexit + import termios + import fcntl + + class Console(ConsoleBase): + def __init__(self): + super(Console, self).__init__() + self.fd = sys.stdin.fileno() + self.old = termios.tcgetattr(self.fd) + atexit.register(self.cleanup) + if sys.version_info < (3, 0): + self.enc_stdin = codecs.getreader(sys.stdin.encoding)(sys.stdin) + else: + self.enc_stdin = sys.stdin + + def setup(self): + new = termios.tcgetattr(self.fd) + new[3] = new[3] & ~termios.ICANON & ~termios.ECHO & ~termios.ISIG + new[6][termios.VMIN] = 1 + new[6][termios.VTIME] = 0 + termios.tcsetattr(self.fd, termios.TCSANOW, new) + + def getkey(self): + c = self.enc_stdin.read(1) + if c == unichr(0x7f): + c = unichr(8) # map the BS key (which yields DEL) to backspace + return c + + def cancel(self): + fcntl.ioctl(self.fd, termios.TIOCSTI, b'\0') + + def cleanup(self): + termios.tcsetattr(self.fd, termios.TCSAFLUSH, self.old) + +else: + raise NotImplementedError( + 'Sorry no implementation for your platform ({}) available.'.format(sys.platform)) + + +# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + +class Transform(object): + """do-nothing: forward all data unchanged""" + def rx(self, text): + """text received from serial port""" + return text + + def tx(self, text): + """text to be sent to serial port""" + return text + + def echo(self, text): + """text to be sent but displayed on console""" + return text + + +class CRLF(Transform): + """ENTER sends CR+LF""" + + def tx(self, text): + return text.replace('\n', '\r\n') + + +class CR(Transform): + """ENTER sends CR""" + + def rx(self, text): + return text.replace('\r', '\n') + + def tx(self, text): + return text.replace('\n', '\r') + + +class LF(Transform): + """ENTER sends LF""" + + +class NoTerminal(Transform): + """remove typical terminal control codes from input""" + + REPLACEMENT_MAP = dict((x, 0x2400 + x) for x in range(32) if unichr(x) not in '\r\n\b\t') + REPLACEMENT_MAP.update( + { + 0x7F: 0x2421, # DEL + 0x9B: 0x2425, # CSI + }) + + def rx(self, text): + return text.translate(self.REPLACEMENT_MAP) + + echo = rx + + +class NoControls(NoTerminal): + """Remove all control codes, incl. CR+LF""" + + REPLACEMENT_MAP = dict((x, 0x2400 + x) for x in range(32)) + REPLACEMENT_MAP.update( + { + 0x20: 0x2423, # visual space + 0x7F: 0x2421, # DEL + 0x9B: 0x2425, # CSI + }) + + +class Printable(Transform): + """Show decimal code for all non-ASCII characters and replace most control codes""" + + def rx(self, text): + r = [] + for c in text: + if ' ' <= c < '\x7f' or c in '\r\n\b\t': + r.append(c) + elif c < ' ': + r.append(unichr(0x2400 + ord(c))) + else: + r.extend(unichr(0x2080 + ord(d) - 48) for d in '{:d}'.format(ord(c))) + r.append(' ') + return ''.join(r) + + echo = rx + + +class Colorize(Transform): + """Apply different colors for received and echo""" + + def __init__(self): + # XXX make it configurable, use colorama? + self.input_color = '\x1b[37m' + self.echo_color = '\x1b[31m' + + def rx(self, text): + return self.input_color + text + + def echo(self, text): + return self.echo_color + text + + +class DebugIO(Transform): + """Print what is sent and received""" + + def rx(self, text): + sys.stderr.write(' [RX:{}] '.format(repr(text))) + sys.stderr.flush() + return text + + def tx(self, text): + sys.stderr.write(' [TX:{}] '.format(repr(text))) + sys.stderr.flush() + return text + + +# other ideas: +# - add date/time for each newline +# - insert newline after: a) timeout b) packet end character + +EOL_TRANSFORMATIONS = { + 'crlf': CRLF, + 'cr': CR, + 'lf': LF, +} + +TRANSFORMATIONS = { + 'direct': Transform, # no transformation + 'default': NoTerminal, + 'nocontrol': NoControls, + 'printable': Printable, + 'colorize': Colorize, + 'debug': DebugIO, +} + + +# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +def ask_for_port(): + """\ + Show a list of ports and ask the user for a choice. To make selection + easier on systems with long device names, also allow the input of an + index. + """ + sys.stderr.write('\n--- Available ports:\n') + ports = [] + for n, (port, desc, hwid) in enumerate(sorted(comports()), 1): + sys.stderr.write('--- {:2}: {:20} {!r}\n'.format(n, port, desc)) + ports.append(port) + while True: + port = raw_input('--- Enter port index or full name: ') + try: + index = int(port) - 1 + if not 0 <= index < len(ports): + sys.stderr.write('--- Invalid index!\n') + continue + except ValueError: + pass + else: + port = ports[index] + return port + + +class Miniterm(object): + """\ + Terminal application. Copy data from serial port to console and vice versa. + Handle special keys from the console to show menu etc. + """ + + def __init__(self, serial_instance, echo=False, eol='crlf', filters=()): + self.console = Console() + self.serial = serial_instance + self.echo = echo + self.raw = False + self.input_encoding = 'UTF-8' + self.output_encoding = 'UTF-8' + self.eol = eol + self.filters = filters + self.update_transformations() + self.exit_character = 0x1d # GS/CTRL+] + self.menu_character = 0x14 # Menu: CTRL+T + self.alive = None + self._reader_alive = None + self.receiver_thread = None + self.rx_decoder = None + self.tx_decoder = None + + def _start_reader(self): + """Start reader thread""" + self._reader_alive = True + # start serial->console thread + self.receiver_thread = threading.Thread(target=self.reader, name='rx') + self.receiver_thread.daemon = True + self.receiver_thread.start() + + def _stop_reader(self): + """Stop reader thread only, wait for clean exit of thread""" + self._reader_alive = False + if hasattr(self.serial, 'cancel_read'): + self.serial.cancel_read() + self.receiver_thread.join() + + def start(self): + """start worker threads""" + self.alive = True + self._start_reader() + # enter console->serial loop + self.transmitter_thread = threading.Thread(target=self.writer, name='tx') + self.transmitter_thread.daemon = True + self.transmitter_thread.start() + self.console.setup() + + def stop(self): + """set flag to stop worker threads""" + self.alive = False + + def join(self, transmit_only=False): + """wait for worker threads to terminate""" + self.transmitter_thread.join() + if not transmit_only: + if hasattr(self.serial, 'cancel_read'): + self.serial.cancel_read() + self.receiver_thread.join() + + def close(self): + self.serial.close() + + def update_transformations(self): + """take list of transformation classes and instantiate them for rx and tx""" + transformations = [EOL_TRANSFORMATIONS[self.eol]] + [TRANSFORMATIONS[f] + for f in self.filters] + self.tx_transformations = [t() for t in transformations] + self.rx_transformations = list(reversed(self.tx_transformations)) + + def set_rx_encoding(self, encoding, errors='replace'): + """set encoding for received data""" + self.input_encoding = encoding + self.rx_decoder = codecs.getincrementaldecoder(encoding)(errors) + + def set_tx_encoding(self, encoding, errors='replace'): + """set encoding for transmitted data""" + self.output_encoding = encoding + self.tx_encoder = codecs.getincrementalencoder(encoding)(errors) + + def dump_port_settings(self): + """Write current settings to sys.stderr""" + sys.stderr.write("\n--- Settings: {p.name} {p.baudrate},{p.bytesize},{p.parity},{p.stopbits}\n".format( + p=self.serial)) + sys.stderr.write('--- RTS: {:8} DTR: {:8} BREAK: {:8}\n'.format( + ('active' if self.serial.rts else 'inactive'), + ('active' if self.serial.dtr else 'inactive'), + ('active' if self.serial.break_condition else 'inactive'))) + try: + sys.stderr.write('--- CTS: {:8} DSR: {:8} RI: {:8} CD: {:8}\n'.format( + ('active' if self.serial.cts else 'inactive'), + ('active' if self.serial.dsr else 'inactive'), + ('active' if self.serial.ri else 'inactive'), + ('active' if self.serial.cd else 'inactive'))) + except serial.SerialException: + # on RFC 2217 ports, it can happen if no modem state notification was + # yet received. ignore this error. + pass + sys.stderr.write('--- software flow control: {}\n'.format('active' if self.serial.xonxoff else 'inactive')) + sys.stderr.write('--- hardware flow control: {}\n'.format('active' if self.serial.rtscts else 'inactive')) + sys.stderr.write('--- serial input encoding: {}\n'.format(self.input_encoding)) + sys.stderr.write('--- serial output encoding: {}\n'.format(self.output_encoding)) + sys.stderr.write('--- EOL: {}\n'.format(self.eol.upper())) + sys.stderr.write('--- filters: {}\n'.format(' '.join(self.filters))) + + def reader(self): + """loop and copy serial->console""" + try: + while self.alive and self._reader_alive: + # read all that is there or wait for one byte + data = self.serial.read(self.serial.in_waiting or 1) + if data: + if self.raw: + self.console.write_bytes(data) + else: + text = self.rx_decoder.decode(data) + for transformation in self.rx_transformations: + text = transformation.rx(text) + self.console.write(text) + except serial.SerialException: + self.alive = False + self.console.cancel() + raise # XXX handle instead of re-raise? + + def writer(self): + """\ + Loop and copy console->serial until self.exit_character character is + found. When self.menu_character is found, interpret the next key + locally. + """ + menu_active = False + try: + while self.alive: + try: + c = self.console.getkey() + except KeyboardInterrupt: + c = '\x03' + if not self.alive: + break + if menu_active: + self.handle_menu_key(c) + menu_active = False + elif c == self.menu_character: + menu_active = True # next char will be for menu + elif c == self.exit_character: + self.stop() # exit app + break + else: + #~ if self.raw: + text = c + for transformation in self.tx_transformations: + text = transformation.tx(text) + self.serial.write(self.tx_encoder.encode(text)) + if self.echo: + echo_text = c + for transformation in self.tx_transformations: + echo_text = transformation.echo(echo_text) + self.console.write(echo_text) + except: + self.alive = False + raise + + def handle_menu_key(self, c): + """Implement a simple menu / settings""" + if c == self.menu_character or c == self.exit_character: + # Menu/exit character again -> send itself + self.serial.write(self.tx_encoder.encode(c)) + if self.echo: + self.console.write(c) + elif c == '\x15': # CTRL+U -> upload file + self.upload_file() + elif c in '\x08hH?': # CTRL+H, h, H, ? -> Show help + sys.stderr.write(self.get_help_text()) + elif c == '\x12': # CTRL+R -> Toggle RTS + self.serial.rts = not self.serial.rts + sys.stderr.write('--- RTS {} ---\n'.format('active' if self.serial.rts else 'inactive')) + elif c == '\x04': # CTRL+D -> Toggle DTR + self.serial.dtr = not self.serial.dtr + sys.stderr.write('--- DTR {} ---\n'.format('active' if self.serial.dtr else 'inactive')) + elif c == '\x02': # CTRL+B -> toggle BREAK condition + self.serial.break_condition = not self.serial.break_condition + sys.stderr.write('--- BREAK {} ---\n'.format('active' if self.serial.break_condition else 'inactive')) + elif c == '\x05': # CTRL+E -> toggle local echo + self.echo = not self.echo + sys.stderr.write('--- local echo {} ---\n'.format('active' if self.echo else 'inactive')) + elif c == '\x06': # CTRL+F -> edit filters + self.change_filter() + elif c == '\x0c': # CTRL+L -> EOL mode + modes = list(EOL_TRANSFORMATIONS) # keys + eol = modes.index(self.eol) + 1 + if eol >= len(modes): + eol = 0 + self.eol = modes[eol] + sys.stderr.write('--- EOL: {} ---\n'.format(self.eol.upper())) + self.update_transformations() + elif c == '\x01': # CTRL+A -> set encoding + self.change_encoding() + elif c == '\x09': # CTRL+I -> info + self.dump_port_settings() + #~ elif c == '\x01': # CTRL+A -> cycle escape mode + #~ elif c == '\x0c': # CTRL+L -> cycle linefeed mode + elif c in 'pP': # P -> change port + self.change_port() + elif c in 'sS': # S -> suspend / open port temporarily + self.suspend_port() + elif c in 'bB': # B -> change baudrate + self.change_baudrate() + elif c == '8': # 8 -> change to 8 bits + self.serial.bytesize = serial.EIGHTBITS + self.dump_port_settings() + elif c == '7': # 7 -> change to 8 bits + self.serial.bytesize = serial.SEVENBITS + self.dump_port_settings() + elif c in 'eE': # E -> change to even parity + self.serial.parity = serial.PARITY_EVEN + self.dump_port_settings() + elif c in 'oO': # O -> change to odd parity + self.serial.parity = serial.PARITY_ODD + self.dump_port_settings() + elif c in 'mM': # M -> change to mark parity + self.serial.parity = serial.PARITY_MARK + self.dump_port_settings() + elif c in 'sS': # S -> change to space parity + self.serial.parity = serial.PARITY_SPACE + self.dump_port_settings() + elif c in 'nN': # N -> change to no parity + self.serial.parity = serial.PARITY_NONE + self.dump_port_settings() + elif c == '1': # 1 -> change to 1 stop bits + self.serial.stopbits = serial.STOPBITS_ONE + self.dump_port_settings() + elif c == '2': # 2 -> change to 2 stop bits + self.serial.stopbits = serial.STOPBITS_TWO + self.dump_port_settings() + elif c == '3': # 3 -> change to 1.5 stop bits + self.serial.stopbits = serial.STOPBITS_ONE_POINT_FIVE + self.dump_port_settings() + elif c in 'xX': # X -> change software flow control + self.serial.xonxoff = (c == 'X') + self.dump_port_settings() + elif c in 'rR': # R -> change hardware flow control + self.serial.rtscts = (c == 'R') + self.dump_port_settings() + else: + sys.stderr.write('--- unknown menu character {} --\n'.format(key_description(c))) + + def upload_file(self): + """Ask user for filenname and send its contents""" + sys.stderr.write('\n--- File to upload: ') + sys.stderr.flush() + with self.console: + filename = sys.stdin.readline().rstrip('\r\n') + if filename: + try: + with open(filename, 'rb') as f: + sys.stderr.write('--- Sending file {} ---\n'.format(filename)) + while True: + block = f.read(1024) + if not block: + break + self.serial.write(block) + # Wait for output buffer to drain. + self.serial.flush() + sys.stderr.write('.') # Progress indicator. + sys.stderr.write('\n--- File {} sent ---\n'.format(filename)) + except IOError as e: + sys.stderr.write('--- ERROR opening file {}: {} ---\n'.format(filename, e)) + + def change_filter(self): + """change the i/o transformations""" + sys.stderr.write('\n--- Available Filters:\n') + sys.stderr.write('\n'.join( + '--- {:<10} = {.__doc__}'.format(k, v) + for k, v in sorted(TRANSFORMATIONS.items()))) + sys.stderr.write('\n--- Enter new filter name(s) [{}]: '.format(' '.join(self.filters))) + with self.console: + new_filters = sys.stdin.readline().lower().split() + if new_filters: + for f in new_filters: + if f not in TRANSFORMATIONS: + sys.stderr.write('--- unknown filter: {}\n'.format(repr(f))) + break + else: + self.filters = new_filters + self.update_transformations() + sys.stderr.write('--- filters: {}\n'.format(' '.join(self.filters))) + + def change_encoding(self): + """change encoding on the serial port""" + sys.stderr.write('\n--- Enter new encoding name [{}]: '.format(self.input_encoding)) + with self.console: + new_encoding = sys.stdin.readline().strip() + if new_encoding: + try: + codecs.lookup(new_encoding) + except LookupError: + sys.stderr.write('--- invalid encoding name: {}\n'.format(new_encoding)) + else: + self.set_rx_encoding(new_encoding) + self.set_tx_encoding(new_encoding) + sys.stderr.write('--- serial input encoding: {}\n'.format(self.input_encoding)) + sys.stderr.write('--- serial output encoding: {}\n'.format(self.output_encoding)) + + def change_baudrate(self): + """change the baudrate""" + sys.stderr.write('\n--- Baudrate: ') + sys.stderr.flush() + with self.console: + backup = self.serial.baudrate + try: + self.serial.baudrate = int(sys.stdin.readline().strip()) + except ValueError as e: + sys.stderr.write('--- ERROR setting baudrate: {} ---\n'.format(e)) + self.serial.baudrate = backup + else: + self.dump_port_settings() + + def change_port(self): + """Have a conversation with the user to change the serial port""" + with self.console: + try: + port = ask_for_port() + except KeyboardInterrupt: + port = None + if port and port != self.serial.port: + # reader thread needs to be shut down + self._stop_reader() + # save settings + settings = self.serial.getSettingsDict() + try: + new_serial = serial.serial_for_url(port, do_not_open=True) + # restore settings and open + new_serial.applySettingsDict(settings) + new_serial.rts = self.serial.rts + new_serial.dtr = self.serial.dtr + new_serial.open() + new_serial.break_condition = self.serial.break_condition + except Exception as e: + sys.stderr.write('--- ERROR opening new port: {} ---\n'.format(e)) + new_serial.close() + else: + self.serial.close() + self.serial = new_serial + sys.stderr.write('--- Port changed to: {} ---\n'.format(self.serial.port)) + # and restart the reader thread + self._start_reader() + + def suspend_port(self): + """\ + open port temporarily, allow reconnect, exit and port change to get + out of the loop + """ + # reader thread needs to be shut down + self._stop_reader() + self.serial.close() + sys.stderr.write('\n--- Port closed: {} ---\n'.format(self.serial.port)) + do_change_port = False + while not self.serial.is_open: + sys.stderr.write('--- Quit: {exit} | p: port change | any other key to reconnect ---\n'.format( + exit=key_description(self.exit_character))) + k = self.console.getkey() + if k == self.exit_character: + self.stop() # exit app + break + elif k in 'pP': + do_change_port = True + break + try: + self.serial.open() + except Exception as e: + sys.stderr.write('--- ERROR opening port: {} ---\n'.format(e)) + if do_change_port: + self.change_port() + else: + # and restart the reader thread + self._start_reader() + sys.stderr.write('--- Port opened: {} ---\n'.format(self.serial.port)) + + def get_help_text(self): + """return the help text""" + # help text, starts with blank line! + return """ +--- pySerial ({version}) - miniterm - help +--- +--- {exit:8} Exit program +--- {menu:8} Menu escape key, followed by: +--- Menu keys: +--- {menu:7} Send the menu character itself to remote +--- {exit:7} Send the exit character itself to remote +--- {info:7} Show info +--- {upload:7} Upload file (prompt will be shown) +--- {repr:7} encoding +--- {filter:7} edit filters +--- Toggles: +--- {rts:7} RTS {dtr:7} DTR {brk:7} BREAK +--- {echo:7} echo {eol:7} EOL +--- +--- Port settings ({menu} followed by the following): +--- p change port +--- 7 8 set data bits +--- N E O S M change parity (None, Even, Odd, Space, Mark) +--- 1 2 3 set stop bits (1, 2, 1.5) +--- b change baud rate +--- x X disable/enable software flow control +--- r R disable/enable hardware flow control +""".format(version=getattr(serial, 'VERSION', 'unknown version'), + exit=key_description(self.exit_character), + menu=key_description(self.menu_character), + rts=key_description('\x12'), + dtr=key_description('\x04'), + brk=key_description('\x02'), + echo=key_description('\x05'), + info=key_description('\x09'), + upload=key_description('\x15'), + repr=key_description('\x01'), + filter=key_description('\x06'), + eol=key_description('\x0c')) + + +# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +# default args can be used to override when calling main() from an other script +# e.g to create a miniterm-my-device.py +def main(default_port=None, default_baudrate=9600, default_rts=None, default_dtr=None): + """Command line tool, entry point""" + + import argparse + + parser = argparse.ArgumentParser( + description="Miniterm - A simple terminal program for the serial port.") + + parser.add_argument( + "port", + nargs='?', + help="serial port name ('-' to show port list)", + default=default_port) + + parser.add_argument( + "baudrate", + nargs='?', + type=int, + help="set baud rate, default: %(default)s", + default=default_baudrate) + + group = parser.add_argument_group("port settings") + + group.add_argument( + "--parity", + choices=['N', 'E', 'O', 'S', 'M'], + type=lambda c: c.upper(), + help="set parity, one of {N E O S M}, default: N", + default='N') + + group.add_argument( + "--rtscts", + action="store_true", + help="enable RTS/CTS flow control (default off)", + default=False) + + group.add_argument( + "--xonxoff", + action="store_true", + help="enable software flow control (default off)", + default=False) + + group.add_argument( + "--rts", + type=int, + help="set initial RTS line state (possible values: 0, 1)", + default=default_rts) + + group.add_argument( + "--dtr", + type=int, + help="set initial DTR line state (possible values: 0, 1)", + default=default_dtr) + + group.add_argument( + "--ask", + action="store_true", + help="ask again for port when open fails", + default=False) + + group = parser.add_argument_group("data handling") + + group.add_argument( + "-e", "--echo", + action="store_true", + help="enable local echo (default off)", + default=False) + + group.add_argument( + "--encoding", + dest="serial_port_encoding", + metavar="CODEC", + help="set the encoding for the serial port (e.g. hexlify, Latin1, UTF-8), default: %(default)s", + default='UTF-8') + + group.add_argument( + "-f", "--filter", + action="append", + metavar="NAME", + help="add text transformation", + default=[]) + + group.add_argument( + "--eol", + choices=['CR', 'LF', 'CRLF'], + type=lambda c: c.upper(), + help="end of line mode", + default='CRLF') + + group.add_argument( + "--raw", + action="store_true", + help="Do no apply any encodings/transformations", + default=False) + + group = parser.add_argument_group("hotkeys") + + group.add_argument( + "--exit-char", + type=int, + metavar='NUM', + help="Unicode of special character that is used to exit the application, default: %(default)s", + default=0x1d) # GS/CTRL+] + + group.add_argument( + "--menu-char", + type=int, + metavar='NUM', + help="Unicode code of special character that is used to control miniterm (menu), default: %(default)s", + default=0x14) # Menu: CTRL+T + + group = parser.add_argument_group("diagnostics") + + group.add_argument( + "-q", "--quiet", + action="store_true", + help="suppress non-error messages", + default=False) + + group.add_argument( + "--develop", + action="store_true", + help="show Python traceback on error", + default=False) + + args = parser.parse_args() + + if args.menu_char == args.exit_char: + parser.error('--exit-char can not be the same as --menu-char') + + if args.filter: + if 'help' in args.filter: + sys.stderr.write('Available filters:\n') + sys.stderr.write('\n'.join( + '{:<10} = {.__doc__}'.format(k, v) + for k, v in sorted(TRANSFORMATIONS.items()))) + sys.stderr.write('\n') + sys.exit(1) + filters = args.filter + else: + filters = ['default'] + + while True: + # no port given on command line -> ask user now + if args.port is None or args.port == '-': + try: + args.port = ask_for_port() + except KeyboardInterrupt: + sys.stderr.write('\n') + parser.error('user aborted and port is not given') + else: + if not args.port: + parser.error('port is not given') + try: + serial_instance = serial.serial_for_url( + args.port, + args.baudrate, + parity=args.parity, + rtscts=args.rtscts, + xonxoff=args.xonxoff, + do_not_open=True) + + if not hasattr(serial_instance, 'cancel_read'): + # enable timeout for alive flag polling if cancel_read is not available + serial_instance.timeout = 1 + + if args.dtr is not None: + if not args.quiet: + sys.stderr.write('--- forcing DTR {}\n'.format('active' if args.dtr else 'inactive')) + serial_instance.dtr = args.dtr + if args.rts is not None: + if not args.quiet: + sys.stderr.write('--- forcing RTS {}\n'.format('active' if args.rts else 'inactive')) + serial_instance.rts = args.rts + + serial_instance.open() + except serial.SerialException as e: + sys.stderr.write('could not open port {}: {}\n'.format(repr(args.port), e)) + if args.develop: + raise + if not args.ask: + sys.exit(1) + else: + args.port = '-' + else: + break + + miniterm = Miniterm( + serial_instance, + echo=args.echo, + eol=args.eol.lower(), + filters=filters) + miniterm.exit_character = unichr(args.exit_char) + miniterm.menu_character = unichr(args.menu_char) + miniterm.raw = args.raw + miniterm.set_rx_encoding(args.serial_port_encoding) + miniterm.set_tx_encoding(args.serial_port_encoding) + + if not args.quiet: + sys.stderr.write('--- Miniterm on {p.name} {p.baudrate},{p.bytesize},{p.parity},{p.stopbits} ---\n'.format( + p=miniterm.serial)) + sys.stderr.write('--- Quit: {} | Menu: {} | Help: {} followed by {} ---\n'.format( + key_description(miniterm.exit_character), + key_description(miniterm.menu_character), + key_description(miniterm.menu_character), + key_description('\x08'))) + + miniterm.start() + try: + miniterm.join(True) + except KeyboardInterrupt: + pass + if not args.quiet: + sys.stderr.write("\n--- exit ---\n") + miniterm.join() + miniterm.close() + +# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +if __name__ == '__main__': + main() diff --git a/backend/venv/bin/miniterm.pyc b/backend/venv/bin/miniterm.pyc new file mode 100755 index 0000000..07aff4e Binary files /dev/null and b/backend/venv/bin/miniterm.pyc differ diff --git a/backend/venv/lib/python3.6/site-packages/__pycache__/easy_install.cpython-36.pyc b/backend/venv/lib/python3.6/site-packages/__pycache__/easy_install.cpython-36.pyc deleted file mode 100644 index f35ac41..0000000 Binary files a/backend/venv/lib/python3.6/site-packages/__pycache__/easy_install.cpython-36.pyc and /dev/null differ diff --git a/backend/venv/lib/python3.6/site-packages/__pycache__/six.cpython-36.pyc b/backend/venv/lib/python3.6/site-packages/__pycache__/six.cpython-36.pyc index e4b2322..33287ba 100644 Binary files a/backend/venv/lib/python3.6/site-packages/__pycache__/six.cpython-36.pyc and b/backend/venv/lib/python3.6/site-packages/__pycache__/six.cpython-36.pyc differ diff --git a/backend/venv/lib/python3.6/site-packages/pyserial-3.4.dist-info/DESCRIPTION.rst b/backend/venv/lib/python3.6/site-packages/pyserial-3.4.dist-info/DESCRIPTION.rst new file mode 100644 index 0000000..606dcaa --- /dev/null +++ b/backend/venv/lib/python3.6/site-packages/pyserial-3.4.dist-info/DESCRIPTION.rst @@ -0,0 +1,13 @@ +Python Serial Port Extension for Win32, OSX, Linux, BSD, Jython, IronPython + +Stable: + +- Documentation: http://pythonhosted.org/pyserial/ +- Download Page: https://pypi.python.org/pypi/pyserial + +Latest: + +- Documentation: http://pyserial.readthedocs.io/en/latest/ +- Project Homepage: https://github.com/pyserial/pyserial + + diff --git a/backend/venv/lib/python3.6/site-packages/serial-0.0.91.dist-info/INSTALLER b/backend/venv/lib/python3.6/site-packages/pyserial-3.4.dist-info/INSTALLER similarity index 100% rename from backend/venv/lib/python3.6/site-packages/serial-0.0.91.dist-info/INSTALLER rename to backend/venv/lib/python3.6/site-packages/pyserial-3.4.dist-info/INSTALLER diff --git a/backend/venv/lib/python3.6/site-packages/pyserial-3.4.dist-info/METADATA b/backend/venv/lib/python3.6/site-packages/pyserial-3.4.dist-info/METADATA new file mode 100644 index 0000000..d9aef45 --- /dev/null +++ b/backend/venv/lib/python3.6/site-packages/pyserial-3.4.dist-info/METADATA @@ -0,0 +1,44 @@ +Metadata-Version: 2.0 +Name: pyserial +Version: 3.4 +Summary: Python Serial Port Extension +Home-page: https://github.com/pyserial/pyserial +Author: Chris Liechti +Author-email: cliechti@gmx.net +License: BSD +Platform: any +Classifier: Development Status :: 5 - Production/Stable +Classifier: Intended Audience :: Developers +Classifier: Intended Audience :: End Users/Desktop +Classifier: License :: OSI Approved :: BSD License +Classifier: Natural Language :: English +Classifier: Operating System :: POSIX +Classifier: Operating System :: Microsoft :: Windows +Classifier: Operating System :: MacOS :: MacOS X +Classifier: Programming Language :: Python +Classifier: Programming Language :: Python :: 2 +Classifier: Programming Language :: Python :: 2.7 +Classifier: Programming Language :: Python :: 3 +Classifier: Programming Language :: Python :: 3.2 +Classifier: Programming Language :: Python :: 3.3 +Classifier: Programming Language :: Python :: 3.4 +Classifier: Programming Language :: Python :: 3.5 +Classifier: Programming Language :: Python :: 3.6 +Classifier: Topic :: Communications +Classifier: Topic :: Software Development :: Libraries +Classifier: Topic :: Software Development :: Libraries :: Python Modules +Classifier: Topic :: Terminals :: Serial + +Python Serial Port Extension for Win32, OSX, Linux, BSD, Jython, IronPython + +Stable: + +- Documentation: http://pythonhosted.org/pyserial/ +- Download Page: https://pypi.python.org/pypi/pyserial + +Latest: + +- Documentation: http://pyserial.readthedocs.io/en/latest/ +- Project Homepage: https://github.com/pyserial/pyserial + + diff --git a/backend/venv/lib/python3.6/site-packages/pyserial-3.4.dist-info/RECORD b/backend/venv/lib/python3.6/site-packages/pyserial-3.4.dist-info/RECORD new file mode 100644 index 0000000..6edc109 --- /dev/null +++ b/backend/venv/lib/python3.6/site-packages/pyserial-3.4.dist-info/RECORD @@ -0,0 +1,91 @@ +../../../bin/__pycache__/miniterm.cpython-36.pyc,, +../../../bin/miniterm.py,sha256=XmdvozzNwHh8zd6tDMoefXHz39myqwvc63L3b6mnS0g,35088 +../../../bin/miniterm.pyc,sha256=cjhQp6L0aitsx6yg5IpG9lYPVzBAYkrSndVHpGwYorY,28907 +pyserial-3.4.dist-info/DESCRIPTION.rst,sha256=rXXIUFeAsfXq2YS7DGkztGmXez-G7gAwbwdBL8t9KME,320 +pyserial-3.4.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 +pyserial-3.4.dist-info/METADATA,sha256=CExk0lotyj8ds3rAKUq2ME1vN0lsYWn3CWPDjFqzLH8,1563 +pyserial-3.4.dist-info/RECORD,, +pyserial-3.4.dist-info/WHEEL,sha256=o2k-Qa-RMNIJmUdIc7KU6VWR_ErNRbWNlxDIpl7lm34,110 +pyserial-3.4.dist-info/metadata.json,sha256=fUPYvHyeQugtZXmQ7u6u8vJOn7CGHArr__PtrUQrg0w,1280 +pyserial-3.4.dist-info/top_level.txt,sha256=FSjfWHWw-VjPiEqOhttbiP-F8OHn-liixq1wKL2fWOA,7 +serial/__init__.py,sha256=M6W0AbWpV-Y6fyw9eb56C2hahVw7MIvWBefT6yCAHIE,3172 +serial/__init__.pyc,sha256=CajVqMkMYQHB_krKsRE8Rz1x_IeWtEz7NUD0oKFZ0tk,2322 +serial/__pycache__/__init__.cpython-36.pyc,, +serial/__pycache__/aio.cpython-36.pyc,, +serial/__pycache__/rfc2217.cpython-36.pyc,, +serial/__pycache__/rs485.cpython-36.pyc,, +serial/__pycache__/serialcli.cpython-36.pyc,, +serial/__pycache__/serialjava.cpython-36.pyc,, +serial/__pycache__/serialposix.cpython-36.pyc,, +serial/__pycache__/serialutil.cpython-36.pyc,, +serial/__pycache__/serialwin32.cpython-36.pyc,, +serial/__pycache__/win32.cpython-36.pyc,, +serial/aio.py,sha256=6gWn7JXv21B1jE-Ykf-7v17y8AA1pha1cEp-OXmCmDU,3659 +serial/aio.pyc,sha256=t5P_mbq_c2SDIDwqG88UbKGMKYeNYh77wqZC3k-vyMQ,4496 +serial/rfc2217.py,sha256=FLgI3m-gx53aH1vISC7P8okCyoagN80Nh1tC6t7hisc,59486 +serial/rfc2217.pyc,sha256=y8gblsIrXc0OUU-HZV-QRhJ138n5a2qbQqi1eht-hs0,37632 +serial/rs485.py,sha256=O2YbxLMlziEX1N39WOukrlhMWcYzVwiP95FBhMmUIHc,3265 +serial/rs485.pyc,sha256=9pJKRaRQLI-aLwWRdJ-5LObGezVeSO45M6AollCHjnM,3266 +serial/serialcli.py,sha256=MVzD-mB-snJhwY4hjXVAuOWA3OXjFU79A2zMMlAMPKQ,9104 +serial/serialcli.pyc,sha256=CqL9FtRa1Wz15V428jnBaukO-7KKn5_4q1t0z713C7s,8308 +serial/serialjava.py,sha256=OsYX1HEEo0pJzRJqmJfJLqZdBFhCO2s8LLt1s1-TYVg,8414 +serial/serialjava.pyc,sha256=fDM30MRvDuytk1Ss8rfK88Gk-a3KvYhhvGWNks7-a2U,9577 +serial/serialposix.py,sha256=UYZDZcu5uCNHjZJfnJ1IUhTXLz-wyV2n0AB0hFd0qxE,31654 +serial/serialposix.pyc,sha256=UOz_nIhtX_D6cpXqQXeGzZBIX4_i6R-xhR9NWiv_K8I,24286 +serial/serialutil.py,sha256=V-AeEMJyl8GvijaiildLRY8k-IOAZpW-YeBxHV2iDvg,21692 +serial/serialutil.pyc,sha256=BO3_7SlWWYxCbYOmerYsQOiLu7XRbda4kkrZx4wIZWw,20027 +serial/serialwin32.py,sha256=tMa-81Oh1c4g5nHf86v2eDnLKb7JHAG-vEQZfq2q_Jo,20087 +serial/serialwin32.pyc,sha256=FArzpAX1_F6xFQmkAbDmaYUbw0wEPWRr39lkXuoHPVA,14471 +serial/threaded/__init__.py,sha256=EHay8pD__ePKaa-WvweNjWe9V1SUgZMefNnayVyiwRw,9272 +serial/threaded/__pycache__/__init__.cpython-36.pyc,, +serial/tools/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +serial/tools/__init__.pyc,sha256=stNDuwUOHX5ySsWZfedBkax1go_wefynkMBqnkv9DSI,140 +serial/tools/__pycache__/__init__.cpython-36.pyc,, +serial/tools/__pycache__/hexlify_codec.cpython-36.pyc,, +serial/tools/__pycache__/list_ports.cpython-36.pyc,, +serial/tools/__pycache__/list_ports_common.cpython-36.pyc,, +serial/tools/__pycache__/list_ports_linux.cpython-36.pyc,, +serial/tools/__pycache__/list_ports_osx.cpython-36.pyc,, +serial/tools/__pycache__/list_ports_posix.cpython-36.pyc,, +serial/tools/__pycache__/list_ports_windows.cpython-36.pyc,, +serial/tools/__pycache__/miniterm.cpython-36.pyc,, +serial/tools/hexlify_codec.py,sha256=1lvd2TFWYUsYMRt7nsyw0VfHTRfl57Hl6kdCmNhMkC4,3637 +serial/tools/hexlify_codec.pyc,sha256=xy_EM_EuW4LzSO3BhEAs7V7HhCxb6e-Iv3KafY9Ckrw,4872 +serial/tools/list_ports.py,sha256=mfNJILSFufMJR2Ns7AWF7CMMhSqXcuK-R-cyK4xBLro,3349 +serial/tools/list_ports.pyc,sha256=Czc32SiTfXsqIC3s2gtZoQNS8i0AoQ6iTBakY84R2-s,2815 +serial/tools/list_ports_common.py,sha256=H_NcNxOc15MnzQWGlIXi0mRiLQV1QNiO6fHjPQ3SXEY,3319 +serial/tools/list_ports_common.pyc,sha256=gE-B4IMXiG2w2mUybAEyrCcSkmuGYcCkcbKhE7tbIm4,3315 +serial/tools/list_ports_linux.py,sha256=-uPj714Jb2Iu3NzFjpnFBEYLCaL3eQG6_iuXj1p9tp4,4427 +serial/tools/list_ports_linux.pyc,sha256=BJ7V6Bu44dKh2PcTK3dDyIhU8qPzjaPxPnqmypLNepI,3038 +serial/tools/list_ports_osx.py,sha256=h0AaNNnMgRWX2VBfQCWpeAlpynA8YUVUblHa8krdoTY,9302 +serial/tools/list_ports_osx.pyc,sha256=FQYKsoGne9LNzhwIM8WFOzvhhR8dYzgPHLOgOcKK1gQ,7164 +serial/tools/list_ports_posix.py,sha256=6z6XZKrjsHkORJe1bn4_loVcgKrkmZCWtSPikrST6y8,4495 +serial/tools/list_ports_posix.pyc,sha256=bpGgc6CIIb3JHvXe14LF6bWx7dT4_yyv2XCdYaya6q0,3966 +serial/tools/list_ports_windows.py,sha256=yHlGXUuCtaBMt3ucU7WeYL27IAHkvrB732ZDiT8TUgA,11336 +serial/tools/list_ports_windows.pyc,sha256=X47GTdA5jDWxd013ilynOgxsYP_vffQfOiJUJEIdxVg,6912 +serial/tools/miniterm.py,sha256=t3rlJRihyYzMGO0EAz0Kpd0sewWYnS6M8qtw6ygqg_g,35101 +serial/tools/miniterm.pyc,sha256=qNPk-vOUXoPDpswiQE4R0LEybrRBzmNBPWV1XglLhTc,30469 +serial/urlhandler/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +serial/urlhandler/__init__.pyc,sha256=SBlUmYLyKiul4P6w70w1c0vIiRpv8MGzn5awpHwaO3o,145 +serial/urlhandler/__pycache__/__init__.cpython-36.pyc,, +serial/urlhandler/__pycache__/protocol_alt.cpython-36.pyc,, +serial/urlhandler/__pycache__/protocol_hwgrep.cpython-36.pyc,, +serial/urlhandler/__pycache__/protocol_loop.cpython-36.pyc,, +serial/urlhandler/__pycache__/protocol_rfc2217.cpython-36.pyc,, +serial/urlhandler/__pycache__/protocol_serve-rfc2217.cpython-36.pyc,, +serial/urlhandler/__pycache__/protocol_socket.cpython-36.pyc,, +serial/urlhandler/__pycache__/protocol_spy.cpython-36.pyc,, +serial/urlhandler/protocol_alt.py,sha256=pWlKn8HBBENU9DNeO46gLgTTWvYHLaLB9O5YVwfEQ0o,1993 +serial/urlhandler/protocol_hwgrep.py,sha256=tx4r6Y8smyF7rV0tb2OXMV3ws7xpJtImBH57ZUaSWIo,3119 +serial/urlhandler/protocol_hwgrep.pyc,sha256=cW_46V23glPYWPwp17_zbxxcZJyJEvEOuoZzosyR11o,1627 +serial/urlhandler/protocol_loop.py,sha256=FoDnQw69tQnPRz8Q7p7PMhxIL1wlBKBi7oAPBwJ9-Xo,10119 +serial/urlhandler/protocol_loop.pyc,sha256=g7QJtSdFQBBNZQwHlgABtxYtWZbOMUH92KuKjZmJDEA,9394 +serial/urlhandler/protocol_rfc2217.py,sha256=886ex7FHRb_qPKXJC7HcR6RtMdmvxo4ChEohliqNs5E,277 +serial/urlhandler/protocol_rfc2217.pyc,sha256=Yb3Cn9QymSWgHiiU3s21_yQiKyOKOgjd3nTRIrkvY4Q,214 +serial/urlhandler/protocol_serve-rfc2217.py,sha256=886ex7FHRb_qPKXJC7HcR6RtMdmvxo4ChEohliqNs5E,277 +serial/urlhandler/protocol_socket.py,sha256=cCy3zC4WzclIHnYQaf0MaW65h22TPRJpEGbMmJhXPyk,14130 +serial/urlhandler/protocol_socket.pyc,sha256=KoUDgHAXrHpKi8NC5ABBZuscVOe8dIC-URq5iWGTWys,9803 +serial/urlhandler/protocol_spy.py,sha256=9DxcDfV0upKmH3beYBcHMSQzLcxRS9ptnx7a__vefUg,9024 +serial/urlhandler/protocol_spy.pyc,sha256=laRaXOAPYzcgG05c68fWObXgVJBJLL5g9CfqOMAdr8I,10984 +serial/win32.py,sha256=-GiHVB-QFrQfD4vHelhaEccwfW__6kC2S_46lIYLVsM,10853 +serial/win32.pyc,sha256=iZof5kxLw1eLsxzaD_O2xgIXs9TVFao_zlyqN8C5fZ8,7725 diff --git a/backend/venv/lib/python3.6/site-packages/serial-0.0.91.dist-info/WHEEL b/backend/venv/lib/python3.6/site-packages/pyserial-3.4.dist-info/WHEEL similarity index 70% rename from backend/venv/lib/python3.6/site-packages/serial-0.0.91.dist-info/WHEEL rename to backend/venv/lib/python3.6/site-packages/pyserial-3.4.dist-info/WHEEL index 1001235..8b6dd1b 100644 --- a/backend/venv/lib/python3.6/site-packages/serial-0.0.91.dist-info/WHEEL +++ b/backend/venv/lib/python3.6/site-packages/pyserial-3.4.dist-info/WHEEL @@ -1,5 +1,5 @@ Wheel-Version: 1.0 -Generator: bdist_wheel (0.32.1) +Generator: bdist_wheel (0.29.0) Root-Is-Purelib: true Tag: py2-none-any Tag: py3-none-any diff --git a/backend/venv/lib/python3.6/site-packages/pyserial-3.4.dist-info/metadata.json b/backend/venv/lib/python3.6/site-packages/pyserial-3.4.dist-info/metadata.json new file mode 100644 index 0000000..fbbe671 --- /dev/null +++ b/backend/venv/lib/python3.6/site-packages/pyserial-3.4.dist-info/metadata.json @@ -0,0 +1 @@ +{"classifiers": ["Development Status :: 5 - Production/Stable", "Intended Audience :: Developers", "Intended Audience :: End Users/Desktop", "License :: OSI Approved :: BSD License", "Natural Language :: English", "Operating System :: POSIX", "Operating System :: Microsoft :: Windows", "Operating System :: MacOS :: MacOS X", "Programming Language :: Python", "Programming Language :: Python :: 2", "Programming Language :: Python :: 2.7", "Programming Language :: Python :: 3", "Programming Language :: Python :: 3.2", "Programming Language :: Python :: 3.3", "Programming Language :: Python :: 3.4", "Programming Language :: Python :: 3.5", "Programming Language :: Python :: 3.6", "Topic :: Communications", "Topic :: Software Development :: Libraries", "Topic :: Software Development :: Libraries :: Python Modules", "Topic :: Terminals :: Serial"], "extensions": {"python.details": {"contacts": [{"email": "cliechti@gmx.net", "name": "Chris Liechti", "role": "author"}], "document_names": {"description": "DESCRIPTION.rst"}, "project_urls": {"Home": "https://github.com/pyserial/pyserial"}}}, "generator": "bdist_wheel (0.29.0)", "license": "BSD", "metadata_version": "2.0", "name": "pyserial", "platform": "any", "summary": "Python Serial Port Extension", "version": "3.4"} \ No newline at end of file diff --git a/backend/venv/lib/python3.6/site-packages/serial-0.0.91.dist-info/top_level.txt b/backend/venv/lib/python3.6/site-packages/pyserial-3.4.dist-info/top_level.txt similarity index 100% rename from backend/venv/lib/python3.6/site-packages/serial-0.0.91.dist-info/top_level.txt rename to backend/venv/lib/python3.6/site-packages/pyserial-3.4.dist-info/top_level.txt diff --git a/backend/venv/lib/python3.6/site-packages/serial-0.0.91.dist-info/METADATA b/backend/venv/lib/python3.6/site-packages/serial-0.0.91.dist-info/METADATA deleted file mode 100644 index 6c0cd03..0000000 --- a/backend/venv/lib/python3.6/site-packages/serial-0.0.91.dist-info/METADATA +++ /dev/null @@ -1,26 +0,0 @@ -Metadata-Version: 2.1 -Name: serial -Version: 0.0.91 -Summary: A framework for serializing/deserializing JSON/YAML/XML into python class instances and vice versa -Home-page: https://bitbucket.com/davebelais/serial.git -Author: David Belais -Author-email: david@belais.me -License: MIT -Keywords: rest api serialization serialize -Platform: UNKNOWN -Classifier: Development Status :: 2 - Pre-Alpha -Classifier: Intended Audience :: Developers -Classifier: License :: OSI Approved :: MIT License -Classifier: Programming Language :: Python :: 2.7 -Classifier: Programming Language :: Python :: 3 -Classifier: Programming Language :: Python :: 3.4 -Classifier: Programming Language :: Python :: 3.5 -Classifier: Programming Language :: Python :: 3.6 -Classifier: Programming Language :: Python :: 3.7 -Requires-Dist: future (>=0.17.1) -Requires-Dist: pyyaml (>=3.13) -Requires-Dist: iso8601 (>=0.1.12) - -UNKNOWN - - diff --git a/backend/venv/lib/python3.6/site-packages/serial-0.0.91.dist-info/RECORD b/backend/venv/lib/python3.6/site-packages/serial-0.0.91.dist-info/RECORD deleted file mode 100644 index 9b6151d..0000000 --- a/backend/venv/lib/python3.6/site-packages/serial-0.0.91.dist-info/RECORD +++ /dev/null @@ -1,33 +0,0 @@ -serial-0.0.91.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 -serial-0.0.91.dist-info/METADATA,sha256=HIgxcDdfyCfsjlnzIWs9nmFaeMKOHvkxTjv6cBHRSTM,889 -serial-0.0.91.dist-info/RECORD,, -serial-0.0.91.dist-info/WHEEL,sha256=8T8fxefr_r-A79qbOJ9d_AaEgkpCGmEPHc-gpCq5BRg,110 -serial-0.0.91.dist-info/top_level.txt,sha256=FSjfWHWw-VjPiEqOhttbiP-F8OHn-liixq1wKL2fWOA,7 -serial/__init__.py,sha256=93wvMGEaUuBYuXcFk9knZsuJVrU6UxUVAfGyAXBgJ94,472 -serial/__pycache__/__init__.cpython-36.pyc,, -serial/__pycache__/errors.cpython-36.pyc,, -serial/__pycache__/hooks.cpython-36.pyc,, -serial/__pycache__/marshal.cpython-36.pyc,, -serial/__pycache__/meta.cpython-36.pyc,, -serial/__pycache__/model.cpython-36.pyc,, -serial/__pycache__/properties.cpython-36.pyc,, -serial/__pycache__/request.cpython-36.pyc,, -serial/__pycache__/test.cpython-36.pyc,, -serial/abc/__init__.py,sha256=kPltSkxwc2LSzjvmCLdRkou3fk6ND85H0Rq0pR3Sbcc,257 -serial/abc/__pycache__/__init__.cpython-36.pyc,, -serial/abc/__pycache__/model.cpython-36.pyc,, -serial/abc/__pycache__/properties.cpython-36.pyc,, -serial/abc/model.py,sha256=lI-7DDWvFjYHJ03VeZSNDxODdCCQeLia0ixZG5uiymU,6116 -serial/abc/properties.py,sha256=ho1HkWayE4m8SHfqM4O-RvcLjzRql4mQxBm65iOiwcc,3036 -serial/errors.py,sha256=aagqxWtjMLuyvHojOhSK4jspVDcXXKUQ-WavH9iXXmM,5217 -serial/hooks.py,sha256=PH28AQ5VtFbT5IuHS8IVMSjYtEWHR0FlBgREqDESi_U,8749 -serial/marshal.py,sha256=b61GMVRP19h_-DW1yqeu82676ti0ermlsvZEVb09pso,22125 -serial/meta.py,sha256=qXDFjkznp7fB_t4KDTVhLSGKnFfafQEAr9YeO9wtkX4,27529 -serial/model.py,sha256=NzdXq9CvcYNYnn-7wZvmwv1j2c8j7gR8gS6CWMtasq0,41874 -serial/properties.py,sha256=XdZlkjaeyc_7rs6x2BXtVl6pKx5cEaZuuzA9vi0RJuc,29488 -serial/request.py,sha256=hqn0CGzGP9pn5eErrHdiPUllIcVqYe-6YhOgGaT9J1M,14226 -serial/test.py,sha256=xNmDsOCeX7NYqmfxs1aQeB1P_0Fnfd88hyfEWhMPH4E,9273 -serial/utilities/__init__.py,sha256=_k3k8_wm9sZ-jWRNS9Bfi0zaHZWY9by-Ykj2qMmUvNQ,13250 -serial/utilities/__pycache__/__init__.cpython-36.pyc,, -serial/utilities/__pycache__/compatibility.cpython-36.pyc,, -serial/utilities/compatibility.py,sha256=rfAYMsSPUyHP8H2rURgbKW7mCsttBJ0R_NFXjXaZZLg,845 diff --git a/backend/venv/lib/python3.6/site-packages/serial/__init__.py b/backend/venv/lib/python3.6/site-packages/serial/__init__.py index 860b2fe..c24ced8 100644 --- a/backend/venv/lib/python3.6/site-packages/serial/__init__.py +++ b/backend/venv/lib/python3.6/site-packages/serial/__init__.py @@ -1,10 +1,89 @@ -""" -`serial` is an object serialization/deserialization library intended to facilitate authoring of API models which are -readable and introspective, and to expedite code and data validation and testing. `serial` supports JSON, YAML, and -XML. -""" +#!/usr/bin/env python +# +# This is a wrapper module for different platform implementations +# +# This file is part of pySerial. https://github.com/pyserial/pyserial +# (C) 2001-2017 Chris Liechti +# +# SPDX-License-Identifier: BSD-3-Clause -from __future__ import nested_scopes, generators, division, absolute_import, with_statement, \ - print_function, unicode_literals +import sys +import importlib -from . import utilities, abc, model, marshal, errors, properties, meta, hooks, test, request +from serial.serialutil import * +#~ SerialBase, SerialException, to_bytes, iterbytes + +__version__ = '3.4' + +VERSION = __version__ + +# pylint: disable=wrong-import-position +if sys.platform == 'cli': + from serial.serialcli import Serial +else: + import os + # chose an implementation, depending on os + if os.name == 'nt': # sys.platform == 'win32': + from serial.serialwin32 import Serial + elif os.name == 'posix': + from serial.serialposix import Serial, PosixPollSerial, VTIMESerial # noqa + elif os.name == 'java': + from serial.serialjava import Serial + else: + raise ImportError("Sorry: no implementation for your platform ('{}') available".format(os.name)) + + +protocol_handler_packages = [ + 'serial.urlhandler', +] + + +def serial_for_url(url, *args, **kwargs): + """\ + Get an instance of the Serial class, depending on port/url. The port is not + opened when the keyword parameter 'do_not_open' is true, by default it + is. All other parameters are directly passed to the __init__ method when + the port is instantiated. + + The list of package names that is searched for protocol handlers is kept in + ``protocol_handler_packages``. + + e.g. we want to support a URL ``foobar://``. A module + ``my_handlers.protocol_foobar`` is provided by the user. Then + ``protocol_handler_packages.append("my_handlers")`` would extend the search + path so that ``serial_for_url("foobar://"))`` would work. + """ + # check and remove extra parameter to not confuse the Serial class + do_open = not kwargs.pop('do_not_open', False) + # the default is to use the native implementation + klass = Serial + try: + url_lowercase = url.lower() + except AttributeError: + # it's not a string, use default + pass + else: + # if it is an URL, try to import the handler module from the list of possible packages + if '://' in url_lowercase: + protocol = url_lowercase.split('://', 1)[0] + module_name = '.protocol_{}'.format(protocol) + for package_name in protocol_handler_packages: + try: + importlib.import_module(package_name) + handler_module = importlib.import_module(module_name, package_name) + except ImportError: + continue + else: + if hasattr(handler_module, 'serial_class_for_url'): + url, klass = handler_module.serial_class_for_url(url) + else: + klass = handler_module.Serial + break + else: + raise ValueError('invalid URL, protocol {!r} not known'.format(protocol)) + # instantiate and open when desired + instance = klass(None, *args, **kwargs) + instance.port = url + if do_open: + instance.open() + return instance diff --git a/backend/venv/lib/python3.6/site-packages/serial/__init__.pyc b/backend/venv/lib/python3.6/site-packages/serial/__init__.pyc new file mode 100644 index 0000000..7f1122a Binary files /dev/null and b/backend/venv/lib/python3.6/site-packages/serial/__init__.pyc differ diff --git a/backend/venv/lib/python3.6/site-packages/serial/__pycache__/__init__.cpython-36.pyc b/backend/venv/lib/python3.6/site-packages/serial/__pycache__/__init__.cpython-36.pyc index 99cd0ee..a95229a 100644 Binary files a/backend/venv/lib/python3.6/site-packages/serial/__pycache__/__init__.cpython-36.pyc and b/backend/venv/lib/python3.6/site-packages/serial/__pycache__/__init__.cpython-36.pyc differ diff --git a/backend/venv/lib/python3.6/site-packages/serial/__pycache__/aio.cpython-36.pyc b/backend/venv/lib/python3.6/site-packages/serial/__pycache__/aio.cpython-36.pyc new file mode 100644 index 0000000..81f5f7e Binary files /dev/null and b/backend/venv/lib/python3.6/site-packages/serial/__pycache__/aio.cpython-36.pyc differ diff --git a/backend/venv/lib/python3.6/site-packages/serial/__pycache__/errors.cpython-36.pyc b/backend/venv/lib/python3.6/site-packages/serial/__pycache__/errors.cpython-36.pyc deleted file mode 100644 index c32a5be..0000000 Binary files a/backend/venv/lib/python3.6/site-packages/serial/__pycache__/errors.cpython-36.pyc and /dev/null differ diff --git a/backend/venv/lib/python3.6/site-packages/serial/__pycache__/hooks.cpython-36.pyc b/backend/venv/lib/python3.6/site-packages/serial/__pycache__/hooks.cpython-36.pyc deleted file mode 100644 index b481dd5..0000000 Binary files a/backend/venv/lib/python3.6/site-packages/serial/__pycache__/hooks.cpython-36.pyc and /dev/null differ diff --git a/backend/venv/lib/python3.6/site-packages/serial/__pycache__/marshal.cpython-36.pyc b/backend/venv/lib/python3.6/site-packages/serial/__pycache__/marshal.cpython-36.pyc deleted file mode 100644 index 1a3b664..0000000 Binary files a/backend/venv/lib/python3.6/site-packages/serial/__pycache__/marshal.cpython-36.pyc and /dev/null differ diff --git a/backend/venv/lib/python3.6/site-packages/serial/__pycache__/meta.cpython-36.pyc b/backend/venv/lib/python3.6/site-packages/serial/__pycache__/meta.cpython-36.pyc deleted file mode 100644 index 88b2500..0000000 Binary files a/backend/venv/lib/python3.6/site-packages/serial/__pycache__/meta.cpython-36.pyc and /dev/null differ diff --git a/backend/venv/lib/python3.6/site-packages/serial/__pycache__/model.cpython-36.pyc b/backend/venv/lib/python3.6/site-packages/serial/__pycache__/model.cpython-36.pyc deleted file mode 100644 index 32a4675..0000000 Binary files a/backend/venv/lib/python3.6/site-packages/serial/__pycache__/model.cpython-36.pyc and /dev/null differ diff --git a/backend/venv/lib/python3.6/site-packages/serial/__pycache__/properties.cpython-36.pyc b/backend/venv/lib/python3.6/site-packages/serial/__pycache__/properties.cpython-36.pyc deleted file mode 100644 index 62c674f..0000000 Binary files a/backend/venv/lib/python3.6/site-packages/serial/__pycache__/properties.cpython-36.pyc and /dev/null differ diff --git a/backend/venv/lib/python3.6/site-packages/serial/__pycache__/request.cpython-36.pyc b/backend/venv/lib/python3.6/site-packages/serial/__pycache__/request.cpython-36.pyc deleted file mode 100644 index bf36e90..0000000 Binary files a/backend/venv/lib/python3.6/site-packages/serial/__pycache__/request.cpython-36.pyc and /dev/null differ diff --git a/backend/venv/lib/python3.6/site-packages/serial/__pycache__/rfc2217.cpython-36.pyc b/backend/venv/lib/python3.6/site-packages/serial/__pycache__/rfc2217.cpython-36.pyc new file mode 100644 index 0000000..02318a7 Binary files /dev/null and b/backend/venv/lib/python3.6/site-packages/serial/__pycache__/rfc2217.cpython-36.pyc differ diff --git a/backend/venv/lib/python3.6/site-packages/serial/__pycache__/rs485.cpython-36.pyc b/backend/venv/lib/python3.6/site-packages/serial/__pycache__/rs485.cpython-36.pyc new file mode 100644 index 0000000..d68b3d1 Binary files /dev/null and b/backend/venv/lib/python3.6/site-packages/serial/__pycache__/rs485.cpython-36.pyc differ diff --git a/backend/venv/lib/python3.6/site-packages/serial/__pycache__/serialcli.cpython-36.pyc b/backend/venv/lib/python3.6/site-packages/serial/__pycache__/serialcli.cpython-36.pyc new file mode 100644 index 0000000..b12804c Binary files /dev/null and b/backend/venv/lib/python3.6/site-packages/serial/__pycache__/serialcli.cpython-36.pyc differ diff --git a/backend/venv/lib/python3.6/site-packages/serial/__pycache__/serialjava.cpython-36.pyc b/backend/venv/lib/python3.6/site-packages/serial/__pycache__/serialjava.cpython-36.pyc new file mode 100644 index 0000000..2a7ef15 Binary files /dev/null and b/backend/venv/lib/python3.6/site-packages/serial/__pycache__/serialjava.cpython-36.pyc differ diff --git a/backend/venv/lib/python3.6/site-packages/serial/__pycache__/serialposix.cpython-36.pyc b/backend/venv/lib/python3.6/site-packages/serial/__pycache__/serialposix.cpython-36.pyc new file mode 100644 index 0000000..f40016f Binary files /dev/null and b/backend/venv/lib/python3.6/site-packages/serial/__pycache__/serialposix.cpython-36.pyc differ diff --git a/backend/venv/lib/python3.6/site-packages/serial/__pycache__/serialutil.cpython-36.pyc b/backend/venv/lib/python3.6/site-packages/serial/__pycache__/serialutil.cpython-36.pyc new file mode 100644 index 0000000..decafc4 Binary files /dev/null and b/backend/venv/lib/python3.6/site-packages/serial/__pycache__/serialutil.cpython-36.pyc differ diff --git a/backend/venv/lib/python3.6/site-packages/serial/__pycache__/serialwin32.cpython-36.pyc b/backend/venv/lib/python3.6/site-packages/serial/__pycache__/serialwin32.cpython-36.pyc new file mode 100644 index 0000000..0388968 Binary files /dev/null and b/backend/venv/lib/python3.6/site-packages/serial/__pycache__/serialwin32.cpython-36.pyc differ diff --git a/backend/venv/lib/python3.6/site-packages/serial/__pycache__/test.cpython-36.pyc b/backend/venv/lib/python3.6/site-packages/serial/__pycache__/test.cpython-36.pyc deleted file mode 100644 index 7faf905..0000000 Binary files a/backend/venv/lib/python3.6/site-packages/serial/__pycache__/test.cpython-36.pyc and /dev/null differ diff --git a/backend/venv/lib/python3.6/site-packages/serial/__pycache__/win32.cpython-36.pyc b/backend/venv/lib/python3.6/site-packages/serial/__pycache__/win32.cpython-36.pyc new file mode 100644 index 0000000..e5142b3 Binary files /dev/null and b/backend/venv/lib/python3.6/site-packages/serial/__pycache__/win32.cpython-36.pyc differ diff --git a/backend/venv/lib/python3.6/site-packages/serial/abc/__init__.py b/backend/venv/lib/python3.6/site-packages/serial/abc/__init__.py deleted file mode 100644 index fcf351e..0000000 --- a/backend/venv/lib/python3.6/site-packages/serial/abc/__init__.py +++ /dev/null @@ -1,9 +0,0 @@ -# region Backwards Compatibility -from __future__ import nested_scopes, generators, division, absolute_import, with_statement, \ - print_function, unicode_literals -from ..utilities.compatibility import backport - -backport() - -from . import model, properties - diff --git a/backend/venv/lib/python3.6/site-packages/serial/abc/__pycache__/__init__.cpython-36.pyc b/backend/venv/lib/python3.6/site-packages/serial/abc/__pycache__/__init__.cpython-36.pyc deleted file mode 100644 index e041bd1..0000000 Binary files a/backend/venv/lib/python3.6/site-packages/serial/abc/__pycache__/__init__.cpython-36.pyc and /dev/null differ diff --git a/backend/venv/lib/python3.6/site-packages/serial/abc/__pycache__/model.cpython-36.pyc b/backend/venv/lib/python3.6/site-packages/serial/abc/__pycache__/model.cpython-36.pyc deleted file mode 100644 index 9e75de1..0000000 Binary files a/backend/venv/lib/python3.6/site-packages/serial/abc/__pycache__/model.cpython-36.pyc and /dev/null differ diff --git a/backend/venv/lib/python3.6/site-packages/serial/abc/__pycache__/properties.cpython-36.pyc b/backend/venv/lib/python3.6/site-packages/serial/abc/__pycache__/properties.cpython-36.pyc deleted file mode 100644 index b35072e..0000000 Binary files a/backend/venv/lib/python3.6/site-packages/serial/abc/__pycache__/properties.cpython-36.pyc and /dev/null differ diff --git a/backend/venv/lib/python3.6/site-packages/serial/abc/model.py b/backend/venv/lib/python3.6/site-packages/serial/abc/model.py deleted file mode 100644 index e5ee35c..0000000 --- a/backend/venv/lib/python3.6/site-packages/serial/abc/model.py +++ /dev/null @@ -1,250 +0,0 @@ -# Tell the linters what's up: -# pylint:disable=wrong-import-position,consider-using-enumerate,useless-object-inheritance -# mccabe:options:max-complexity=999 -from __future__ import nested_scopes, generators, division, absolute_import, with_statement, \ - print_function, unicode_literals - -from serial.utilities.compatibility import backport - -backport() - -from abc import ABCMeta, abstractmethod - -# We need ABCs to inherit from `ABC` in python 3x, but in python 2x ABC is absent and we need classes to inherit from -# `object` in order to be new-style classes -try: - from abc import ABC -except ImportError: - ABC = object - -from ..utilities import collections - - -class Model(ABC): - - __metaclass__ = ABCMeta - - _format = None # type: Optional[str] - _meta = None # type: Optional[meta.Object] - _hooks = None # type: Optional[hooks.Object] - - @abstractmethod - def __init__(self): - self._format = None # type: Optional[str] - self._meta = None # type: Optional[meta.Meta] - self._hooks = None # type: Optional[hooks.Hooks] - self._url = None # type: Optional[str] - self._xpath = None # type: Optional[str] - self._pointer = None # type: Optional[str] - - @classmethod - def __subclasscheck__(cls, subclass): - # type: (object) -> bool - """ - Check a subclass to ensure it has required properties - """ - - if cls is subclass or type.__subclasscheck__(cls, subclass): - return True - - for attribute in ( - '_format', - '_meta', - '_hooks' - ): - if not hasattr(subclass, attribute): - return False - - return True - - def __instancecheck__(self, instance): - # type: (object) -> bool - """ - Check an instance of a subclass to ensure it has required properties - """ - - for attribute in ( - '_format', - '_meta', - '_hooks', - '_url', - '_xpath', - '_pointer' - ): - if not hasattr(self, attribute): - return False - - # Perform any instance checks needed for our superclass(es) - try: - return super().__instancecheck__(instance) - except AttributeError: # There is no further instance-checking to perform - return True - - @abstractmethod - def _marshal(self): - # type: (...) -> Union[collections.OrderedDict, list] - pass - - @abstractmethod - def _validate(self, raise_errors=True): - # type: (bool) -> None - pass - - @abstractmethod - def __str__(self): - # type: (...) -> str - pass - - @abstractmethod - def __repr__(self): - # type: (...) -> str - pass - - @abstractmethod - def __copy__(self): - # type: (...) -> Object - pass - - @abstractmethod - def __deepcopy__(self, memo): - # type: (Optional[dict]) -> Object - pass - - @abstractmethod - def __eq__(self, other): - # type: (Any) -> bool - pass - - @abstractmethod - def __ne__(self, other): - # type: (Any) -> bool - pass - - -class Object(Model): - - @abstractmethod - def _marshal(self): - # type: (...) -> collections.OrderedDict - pass - - @abstractmethod - def __setitem__(self, key, value): - # type: (str, Any) -> None - pass - - @abstractmethod - def __getitem__(self, key): - # type: (str, Any) -> None - pass - - @abstractmethod - def __setattr__(self, property_name, value): - # type: (Object, str, Any) -> None - pass - - @abstractmethod - def __getattr__(self, key): - # type: (str) -> Any - pass - - @abstractmethod - def __delattr__(self, key): - # type: (str) -> None - pass - - -class Dictionary(Model): - - @classmethod - def __subclasscheck__(cls, subclass): - # type: (object) -> bool - """ - Verify inheritance - """ - if cls is subclass or type.__subclasscheck__(cls, subclass): - return True - - if not issubclass(subclass, collections.OrderedDict): - return False - - # Perform any subclass checks needed for our superclass(es) - return super().__subclasscheck__(subclass) - - def __instancecheck__(self, instance): - # type: (object) -> bool - """ - Check an instance of a subclass to ensure it has required properties - """ - - if not isinstance(self, collections.OrderedDict): - return False - - # Perform any instance checks needed for our superclass(es) - return super().__instancecheck__(instance) - - @abstractmethod - def _marshal(self): - # type: (...) -> collections.OrderedDict - pass - - @abstractmethod - def __setitem__(self, key, value): - # type: (str, Any) -> None - pass - - def keys(self): - # type: (...) -> Iterable[str] - pass - - def values(self): - # type: (...) -> Iterable[Any] - pass - - -class Array(Model): - - @classmethod - def __subclasscheck__(cls, subclass): - # type: (object) -> bool - """ - Verify inheritance - """ - - if cls is subclass or type.__subclasscheck__(cls, subclass): - return True - - if not issubclass(subclass, list): - return False - - # Perform any subclass checks needed for our superclass(es) - return super().__subclasscheck__(subclass) - - def __instancecheck__(self, instance): - # type: (object) -> bool - """ - Check an instance of a subclass to ensure it has required properties - """ - - if not isinstance(self, list): - return False - - # Perform any instance checks needed for our superclass(es) - return super().__instancecheck__(instance) - - @abstractmethod - def _marshal(self): - # type: (...) -> list - pass - - @abstractmethod - def __setitem__(self, key, value): - # type: (str, Any) -> None - pass - - @abstractmethod - def append(self, value): - # type: (Any) -> None - pass - - diff --git a/backend/venv/lib/python3.6/site-packages/serial/abc/properties.py b/backend/venv/lib/python3.6/site-packages/serial/abc/properties.py deleted file mode 100644 index 0db386c..0000000 --- a/backend/venv/lib/python3.6/site-packages/serial/abc/properties.py +++ /dev/null @@ -1,108 +0,0 @@ -# Tell the linters what's up: -# pylint:disable=wrong-import-position,consider-using-enumerate,useless-object-inheritance -# mccabe:options:max-complexity=999 -from __future__ import nested_scopes, generators, division, absolute_import, with_statement, \ - print_function, unicode_literals -from ..utilities.compatibility import backport - -backport() - -from future.utils import native_str # noqa - -from abc import ABCMeta, abstractmethod - -# We need to inherit from `ABC` in python 3x, but in python 2x ABC is absent -try: - from abc import ABC -except ImportError: - ABC = object - - -class Property(ABC): - - __metaclass__ = ABCMeta - - @abstractmethod - def __init__( - self, - types=None, # type: Sequence[Union[type, Property]] - name=None, # type: Optional[str] - required=False, # type: Union[bool, collections.Callable] - versions=None, # type: Optional[Sequence[Union[str, serial.meta.Version]]] - ): - self._types = None # type: Optional[Sequence[Union[type, Property]]] - self.types = types - self.name = name - self.required = required - self._versions = None # type: Optional[Union[Mapping[str, Optional[Property]], Set[Union[str, Number]]]] - self.versions = versions # type: Optional[Union[Mapping[str, Optional[Property]], Set[Union[str, Number]]]] - - def __instancecheck__(self, instance): - # type: (object) -> bool - """ - Check an instance of a subclass to ensure it has required properties - """ - - for attribute in ( - '_types', - '_versions', - 'name', - 'required' - ): - if not hasattr(self, attribute): - return False - - # Perform any instance checks needed for our superclass(es) - return super().__instancecheck__(instance) - - @property - @abstractmethod - def types(self): - # type: (...) -> Optional[Sequence[Union[type, Property, Model]]] - pass - - @types.setter - @abstractmethod - def types(self, types_or_properties): - # type: (Optional[Sequence[Union[type, Property, Model]]]) -> None - pass - - @property - @abstractmethod - def versions(self): - # type: () -> Optional[Sequence[meta.Version]] - pass - - @versions.setter - @abstractmethod - def versions( - self, - versions # type: Optional[Sequence[Union[str, collections_abc.Iterable, meta.Version]]] - ): - # type: (...) -> Optional[Union[Mapping[str, Optional[Property]], Set[Union[str, Number]]]] - pass - - @abstractmethod - def unmarshal(self, data): - # type: (Any) -> Any - pass - - @abstractmethod - def marshal(self, data): - # type: (Any) -> Any - pass - - @abstractmethod - def __repr__(self): - # type: (...) -> str - pass - - @abstractmethod - def __copy__(self): - # type: (...) -> Property - pass - - @abstractmethod - def __deepcopy__(self, memo): - # type: (dict) -> Property - pass diff --git a/backend/venv/lib/python3.6/site-packages/serial/aio.py b/backend/venv/lib/python3.6/site-packages/serial/aio.py new file mode 100644 index 0000000..257c47c --- /dev/null +++ b/backend/venv/lib/python3.6/site-packages/serial/aio.py @@ -0,0 +1,115 @@ +#!/usr/bin/env python3 +# +# Python Serial Port Extension for Win32, Linux, BSD, Jython +# module for serial IO for POSIX compatible systems, like Linux +# see __init__.py +# +# (C) 2015 Chris Liechti +# +# SPDX-License-Identifier: BSD-3-Clause +"""\ +Support asyncio with serial ports. EXPERIMENTAL + +Posix platforms only, Python 3.4+ only. + +Windows event loops can not wait for serial ports with the current +implementation. It should be possible to get that working though. +""" +import asyncio +import serial +import logger + + +class SerialTransport(asyncio.Transport): + def __init__(self, loop, protocol, serial_instance): + self._loop = loop + self._protocol = protocol + self.serial = serial_instance + self._closing = False + self._paused = False + # XXX how to support url handlers too + self.serial.timeout = 0 + self.serial.nonblocking() + loop.call_soon(protocol.connection_made, self) + # only start reading when connection_made() has been called + loop.call_soon(loop.add_reader, self.serial.fd, self._read_ready) + + def __repr__(self): + return '{self.__class__.__name__}({self._loop}, {self._protocol}, {self.serial})'.format(self=self) + + def close(self): + if self._closing: + return + self._closing = True + self._loop.remove_reader(self.serial.fd) + self.serial.close() + self._loop.call_soon(self._protocol.connection_lost, None) + + def _read_ready(self): + data = self.serial.read(1024) + if data: + self._protocol.data_received(data) + + def write(self, data): + self.serial.write(data) + + def can_write_eof(self): + return False + + def pause_reading(self): + if self._closing: + raise RuntimeError('Cannot pause_reading() when closing') + if self._paused: + raise RuntimeError('Already paused') + self._paused = True + self._loop.remove_reader(self._sock_fd) + if self._loop.get_debug(): + logger.debug("%r pauses reading", self) + + def resume_reading(self): + if not self._paused: + raise RuntimeError('Not paused') + self._paused = False + if self._closing: + return + self._loop.add_reader(self._sock_fd, self._read_ready) + if self._loop.get_debug(): + logger.debug("%r resumes reading", self) + + #~ def set_write_buffer_limits(self, high=None, low=None): + #~ def get_write_buffer_size(self): + #~ def writelines(self, list_of_data): + #~ def write_eof(self): + #~ def abort(self): + + +@asyncio.coroutine +def create_serial_connection(loop, protocol_factory, *args, **kwargs): + ser = serial.Serial(*args, **kwargs) + protocol = protocol_factory() + transport = SerialTransport(loop, protocol, ser) + return (transport, protocol) + +# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +# test +if __name__ == '__main__': + class Output(asyncio.Protocol): + def connection_made(self, transport): + self.transport = transport + print('port opened', transport) + transport.serial.rts = False + transport.write(b'hello world\n') + + def data_received(self, data): + print('data received', repr(data)) + self.transport.close() + + def connection_lost(self, exc): + print('port closed') + asyncio.get_event_loop().stop() + + loop = asyncio.get_event_loop() + coro = create_serial_connection(loop, Output, '/dev/ttyUSB0', baudrate=115200) + loop.run_until_complete(coro) + loop.run_forever() + loop.close() diff --git a/backend/venv/lib/python3.6/site-packages/serial/aio.pyc b/backend/venv/lib/python3.6/site-packages/serial/aio.pyc new file mode 100644 index 0000000..3e72025 Binary files /dev/null and b/backend/venv/lib/python3.6/site-packages/serial/aio.pyc differ diff --git a/backend/venv/lib/python3.6/site-packages/serial/errors.py b/backend/venv/lib/python3.6/site-packages/serial/errors.py deleted file mode 100644 index dd952d2..0000000 --- a/backend/venv/lib/python3.6/site-packages/serial/errors.py +++ /dev/null @@ -1,214 +0,0 @@ -from __future__ import nested_scopes, generators, division, absolute_import, with_statement, \ - print_function, unicode_literals -from .utilities.compatibility import backport - -backport() - -from future.utils import native_str - -from datetime import date, datetime -from decimal import Decimal -from numbers import Number - -try: - import typing -except ImportError as e: - typing = None - -from .abc.model import Model -from .utilities import qualified_name, collections, collections_abc, Generator - - -class ValidationError(Exception): - - pass - - -class VersionError(AttributeError): - - pass - - -class DefinitionExistsError(Exception): - - pass - - -class UnmarshalValueError(ValueError): - - pass - - -UNMARSHALLABLE_TYPES = tuple({ - str, bytes, native_str, Number, Decimal, date, datetime, bool, - dict, collections.OrderedDict, - collections_abc.Set, collections_abc.Sequence, Generator, - Model -}) - - -class UnmarshalError(Exception): - - def __init__( - self, - data, # type: Any - types=None, # type: Optional[Sequence[Model, Property, type]] - item_types=None, # type: Optional[Sequence[Model, Property, type]] - value_types=None # type: Optional[Sequence[Model, Property, type]] - ): - # type: (...) -> None - """ - Generate a comprehensible error message for data which could not be un-marshalled according to spec, and raise - the appropriate exception - """ - - self._message = None # type: Optional[str] - self._parameter = None # type: Optional[str] - self._index = None # type: Optional[int] - self._key = None # type: Optional[str] - - error_message_lines = [''] - - # Identify which parameter is being used for type validation - - types_label = None - - if types: - types_label = 'types' - elif item_types: - types_label = 'item_types' - types = item_types - elif value_types: - types_label = 'value_types' - types = value_types - - # Assemble the error message - - # Assemble a text representation of the `data` - - data_lines = [] - - lines = repr(data).strip().split('\n') - - if len(lines) == 1: - - data_lines.append(lines[0]) - - else: - - data_lines.append('') - - for line in lines: - data_lines.append( - ' ' + line - ) - - # Assemble a text representation of the `types`, `item_types`, or `value_types`. - - if types is None: - - error_message_lines.append('The data provided is not an instance of an un-marshallable type:') - - else: - - error_message_lines.append( - 'The data provided does not match any of the expected types and/or property definitions:' - ) - - error_message_lines.append( - ' - data: %s' % '\n'.join(data_lines) - ) - - if types is None: - - types = UNMARSHALLABLE_TYPES - types_label = 'un-marshallable types' - - types_lines = ['('] - - for type_ in types: - - if isinstance(type_, type): - lines = (qualified_name(type_),) - else: - lines = repr(type_).split('\n') - - for line in lines: - types_lines.append( - ' ' + line - ) - - types_lines[-1] += ',' - - types_lines.append(' )') - - error_message_lines.append( - ' - %s: %s' % (types_label, '\n'.join(types_lines)) - ) - - self.message = '\n'.join(error_message_lines) - - @property - def paramater(self): - # type: (...) -> Optional[str] - return self._parameter - - @paramater.setter - def paramater(self, paramater_name): - # type: (str) -> None - if paramater_name != self.paramater: - self._parameter = paramater_name - self.assemble_message() - - @property - def message(self): - # type: (...) -> Optional[str] - return self._message - - @message.setter - def message(self, message_text): - # type: (str) -> None - if message_text != self.message: - self._message = message_text - self.assemble_message() - - @property - def index(self): - # type: (...) -> Optional[int] - return self._message - - @index.setter - def index(self, index_or_key): - # type: (Union[str, int]) -> None - if index_or_key != self.index: - self._index = index_or_key - self.assemble_message() - - def assemble_message(self): - - messages = [] - - if self.paramater: - messages.append( - 'Errors encountered in attempting to un-marshal %s:' % self.paramater - ) - - if self.index is not None: - messages.append( - 'Errors encountered in attempting to un-marshal the value at index %s:' % repr(self.index) - ) - - if self.message: - messages.append(self.message) - - super().__init__('\n'.join(messages)) - - -class UnmarshalTypeError(UnmarshalError, TypeError): - - pass - - -class UnmarshalValueError(UnmarshalError, ValueError): - - pass diff --git a/backend/venv/lib/python3.6/site-packages/serial/hooks.py b/backend/venv/lib/python3.6/site-packages/serial/hooks.py deleted file mode 100644 index 231a800..0000000 --- a/backend/venv/lib/python3.6/site-packages/serial/hooks.py +++ /dev/null @@ -1,258 +0,0 @@ -from __future__ import nested_scopes, generators, division, absolute_import, with_statement, \ - print_function, unicode_literals -from .utilities.compatibility import backport - -backport() - -import serial.abc - -from copy import deepcopy - -import serial -from .utilities import qualified_name -from .abc.model import Model - -try: - import typing -except ImportError as e: - typing = None - - -class Hooks(object): - - def __init__( - self, - before_marshal=None, # Optional[Callable] - after_marshal=None, # Optional[Callable] - before_unmarshal=None, # Optional[Callable] - after_unmarshal=None, # Optional[Callable] - before_serialize=None, # Optional[Callable] - after_serialize=None, # Optional[Callable] - before_deserialize=None, # Optional[Callable] - after_deserialize=None, # Optional[Callable] - before_validate=None, # Optional[Callable] - after_validate=None, # Optional[Callable] - ): - self.before_marshal = before_marshal - self.after_marshal = after_marshal - self.before_unmarshal = before_unmarshal - self.after_unmarshal = after_unmarshal - self.before_serialize = before_serialize - self.after_serialize = after_serialize - self.before_deserialize = before_deserialize - self.after_deserialize = after_deserialize - self.before_validate = before_validate - self.after_validate = after_validate - - def __copy__(self): - return self.__class__(**vars(self)) - - def __deepcopy__(self, memo=None): - # type: (dict) -> Memo - return self.__class__(**{ - k: deepcopy(v, memo=memo) - for k, v in vars(self).items() - }) - - def __bool__(self): - return True - - -class Object(Hooks): - - def __init__( - self, - before_marshal=None, # Optional[Callable] - after_marshal=None, # Optional[Callable] - before_unmarshal=None, # Optional[Callable] - after_unmarshal=None, # Optional[Callable] - before_serialize=None, # Optional[Callable] - after_serialize=None, # Optional[Callable] - before_deserialize=None, # Optional[Callable] - after_deserialize=None, # Optional[Callable] - before_validate=None, # Optional[Callable] - after_validate=None, # Optional[Callable] - before_setattr=None, # Optional[Callable] - after_setattr=None, # Optional[Callable] - before_setitem=None, # Optional[Callable] - after_setitem=None, # Optional[Callable] - ): - self.before_marshal = before_marshal - self.after_marshal = after_marshal - self.before_unmarshal = before_unmarshal - self.after_unmarshal = after_unmarshal - self.before_serialize = before_serialize - self.after_serialize = after_serialize - self.before_deserialize = before_deserialize - self.after_deserialize = after_deserialize - self.before_validate = before_validate - self.after_validate = after_validate - self.before_setattr = before_setattr - self.after_setattr = after_setattr - self.before_setitem = before_setitem - self.after_setitem = after_setitem - - -class Array(Hooks): - - def __init__( - self, - before_marshal=None, # Optional[Callable] - after_marshal=None, # Optional[Callable] - before_unmarshal=None, # Optional[Callable] - after_unmarshal=None, # Optional[Callable] - before_serialize=None, # Optional[Callable] - after_serialize=None, # Optional[Callable] - before_deserialize=None, # Optional[Callable] - after_deserialize=None, # Optional[Callable] - before_validate=None, # Optional[Callable] - after_validate=None, # Optional[Callable] - before_setitem=None, # Optional[Callable] - after_setitem=None, # Optional[Callable] - before_append=None, # Optional[Callable] - after_append=None, # Optional[Callable] - ): - self.before_marshal = before_marshal - self.after_marshal = after_marshal - self.before_unmarshal = before_unmarshal - self.after_unmarshal = after_unmarshal - self.before_serialize = before_serialize - self.after_serialize = after_serialize - self.before_deserialize = before_deserialize - self.after_deserialize = after_deserialize - self.before_validate = before_validate - self.after_validate = after_validate - self.before_setitem = before_setitem - self.after_setitem = after_setitem - self.before_append = before_append - self.after_append = after_append - - -class Dictionary(Hooks): - - def __init__( - self, - before_marshal=None, # Optional[Callable] - after_marshal=None, # Optional[Callable] - before_unmarshal=None, # Optional[Callable] - after_unmarshal=None, # Optional[Callable] - before_serialize=None, # Optional[Callable] - after_serialize=None, # Optional[Callable] - before_deserialize=None, # Optional[Callable] - after_deserialize=None, # Optional[Callable] - before_validate=None, # Optional[Callable] - after_validate=None, # Optional[Callable] - before_setitem=None, # Optional[Callable] - after_setitem=None, # Optional[Callable] - ): - self.before_marshal = before_marshal - self.after_marshal = after_marshal - self.before_unmarshal = before_unmarshal - self.after_unmarshal = after_unmarshal - self.before_serialize = before_serialize - self.after_serialize = after_serialize - self.before_deserialize = before_deserialize - self.after_deserialize = after_deserialize - self.before_validate = before_validate - self.after_validate = after_validate - self.before_setitem = before_setitem - self.after_setitem = after_setitem - - -def read( - model_instance # type: Union[type, serial.abc.model.Model] -): - # type: (...) -> Hooks - """ - Read metadata from a model instance (the returned metadata may be inherited, and therefore should not be written to) - """ - - if isinstance(model_instance, type): - return model_instance._hooks - elif isinstance(model_instance, Model): - return model_instance._hooks or read(type(model_instance)) - - -def writable( - model_instance # type: Union[type, serial.abc.model.Model] -): - # type: (...) -> Hooks - """ - Retrieve a metadata instance. If the instance currently inherits its metadata from a class or superclass, - this funtion will copy that metadata and assign it directly to the model instance. - """ - - if isinstance(model_instance, type): - - if model_instance._hooks is None: - - model_instance._hooks = ( - Object() - if issubclass(model_instance, serial.model.Object) else - Array() - if issubclass(model_instance, serial.model.Array) else - Dictionary() - if issubclass(model_instance, serial.model.Dictionary) - else None - ) - - else: - - for b in model_instance.__bases__: - - if hasattr(b, '_hooks') and (model_instance._hooks is b._hooks): - model_instance._hooks = deepcopy(model_instance._hooks) - break - - elif isinstance(model_instance, Model): - - if model_instance._hooks is None: - model_instance._hooks = deepcopy(writable(type(model_instance))) - - return model_instance._hooks - - -def write( - model_instance, # type: Union[type, serial.abc.model.Model] - meta # type: Hooks -): - # type: (...) -> None - """ - Write metadata to a class or instance - """ - - if isinstance(model_instance, type): - - t = model_instance - mt = ( - Object - if issubclass(model_instance, serial.model.Object) else - Array - if issubclass(model_instance, serial.model.Array) else - Dictionary - if issubclass(model_instance, serial.model.Dictionary) - else None - ) - - elif isinstance(model_instance, Model): - - t = type(model_instance) - mt = ( - Object - if isinstance(model_instance, serial.model.Object) else - Array - if isinstance(model_instance, serial.model.Array) else - Dictionary - if isinstance(model_instance, serial.model.Dictionary) - else None - ) - - if not isinstance(meta, mt): - raise ValueError( - 'Hooks assigned to `%s` must be of type `%s`' % ( - qualified_name(t), - qualified_name(mt) - ) - ) - - model_instance._hooks = meta diff --git a/backend/venv/lib/python3.6/site-packages/serial/marshal.py b/backend/venv/lib/python3.6/site-packages/serial/marshal.py deleted file mode 100644 index 15ecdce..0000000 --- a/backend/venv/lib/python3.6/site-packages/serial/marshal.py +++ /dev/null @@ -1,704 +0,0 @@ -# Tell the linters what's up: -# pylint:disable=wrong-import-position,consider-using-enumerate,useless-object-inheritance -# mccabe:options:max-complexity=999 -from __future__ import nested_scopes, generators, division, absolute_import, with_statement, \ - print_function, unicode_literals - -from .utilities.compatibility import backport - -backport() # noqa - -from future.utils import native_str - -from copy import deepcopy - -import json -from decimal import Decimal -from base64 import b64encode -from numbers import Number - -import yaml -from datetime import date, datetime - -from itertools import chain - -import serial -from . import utilities, abc, properties, errors, meta, hooks -from .utilities import Generator, qualified_name, read, collections, collections_abc - - -try: - from typing import Union, Optional, Sequence, Any, Callable -except ImportError: - Union = Optional = Sequence = Any = Callable = None - -try: - from abc import ABC -except ImportError: - ABC = None - - -UNMARSHALLABLE_TYPES = tuple({ - str, bytes, native_str, Number, Decimal, date, datetime, bool, - dict, collections.OrderedDict, - collections_abc.Set, collections_abc.Sequence, Generator, - abc.model.Model, properties.Null, properties.NoneType -}) - - -def ab2c(abc_or_property): - # type: (ABC) -> Union[model.Object, model.Dict, model.Array, properties.Property] - """ - Get the corresponding model class from an abstract base class, or if none exists--return the original class or type - """ - - class_or_property = abc_or_property # type: (type) - - if isinstance(abc_or_property, type): - - if abc_or_property in {abc.model.Dictionary, abc.model.Array, abc.model.Object}: - - class_or_property = getattr( - serial.model, - abc_or_property.__name__.split('.')[-1] - ) - - elif isinstance(abc_or_property, properties.Property): - - class_or_property = deepcopy(abc_or_property) - - for types_attribute in ('types', 'value_types', 'item_types'): - - if hasattr(class_or_property, types_attribute): - - setattr( - class_or_property, - types_attribute, - tuple( - ab2c(type_) for type_ in getattr(class_or_property, types_attribute) - ) - ) - - return class_or_property - - -def ab2cs(abcs_or_properties): - # type: (Sequence[Union[ABC, properties.Property]]) -> Sequence[model.Model, properties.Property] - for abc_or_property in abcs_or_properties: - yield ab2c(abc_or_property) - - -class _Marshal(object): - - pass - - -def marshal( - data, # type: Any - types=None, # type: Optional[Sequence[Union[type, properties.Property, Callable]]] - value_types=None, # type: Optional[Sequence[Union[type, properties.Property]]] - item_types=None, # type: Optional[Sequence[Union[type, properties.Property]]] -): - # type: (...) -> Any - - """ - Recursively converts instances of `serial.abc.model.Model` into JSON/YAML/XML serializable objects. - """ - - if hasattr(data, '_marshal'): - return data._marshal() # noqa - this is *our* protected member, so linters can buzz off - - if data is None: - return data - - if callable(types): - types = types(data) - - # If data types have been provided, validate the un-marshalled data by attempting to initialize the provided type(s) - # with `data` - - if types is not None: - - if (str in types) and (native_str is not str) and (native_str not in types): - - types = tuple(chain(*( - ((type_, native_str) if (type_ is str) else (type_,)) - for type_ in types - ))) - - matched = False - - for type_ in types: - - if isinstance(type_, properties.Property): - - try: - data = type_.marshal(data) - matched = True - break - except TypeError: - pass - - elif isinstance(type_, type) and isinstance(data, type_): - - matched = True - - break - - if not matched: - raise TypeError( - '%s cannot be interpreted as any of the designated types: %s' % ( - repr(data), - repr(types) - ) - ) - - if value_types is not None: - for k, v in data.items(): - data[k] = marshal(v, types=value_types) - - if item_types is not None: - - for i in range(len(data)): - data[i] = marshal(data[i], types=item_types) - - if isinstance(data, Decimal): - return float(data) - - if isinstance(data, (date, datetime)): - return data.isoformat() - - if isinstance(data, native_str): - return data - - if isinstance(data, (bytes, bytearray)): - return str(b64encode(data), 'ascii') - - if hasattr(data, '__bytes__'): - return str(b64encode(bytes(data)), 'ascii') - - return data - - -class _Unmarshal(object): - """ - This class should be used exclusively by `serial.marshal.unmarshal`. - """ - - def __init__( - self, - data, # type: Any - types=None, # type: Optional[Sequence[Union[type, properties.Property]]] - value_types=None, # type: Optional[Sequence[Union[type, properties.Property]]] - item_types=None # type: Optional[Sequence[Union[type, properties.Property]]] - ): - # type: (...) -> None - - if not isinstance( - data, - UNMARSHALLABLE_TYPES - ): - # Verify that the data can be parsed before attempting to un-marshall it - - raise errors.UnmarshalTypeError( - '%s, an instance of `%s`, cannot be un-marshalled. ' % (repr(data), type(data).__name__) + - 'Acceptable types are: ' + ', '.join(( - qualified_name(data_type) - for data_type in UNMARSHALLABLE_TYPES - )) - ) - - # If only one type was passed for any of the following parameters--we convert it to a tuple - # If any parameters are abstract base classes--we convert them to the corresponding models - - if types is not None: - - if not isinstance(types, collections_abc.Sequence): - types = (types,) - - if value_types is not None: - - if not isinstance(value_types, collections_abc.Sequence): - value_types = (value_types,) - - if item_types is not None: - - if not isinstance(item_types, collections_abc.Sequence): - item_types = (item_types,) - - # Member data - - self.data = data # type: Any - self.types = types # type: Optional[Sequence[Union[type, properties.Property]]] - self.value_types = value_types # type: Optional[Sequence[Union[type, properties.Property]]] - self.item_types = item_types # type: Optional[Sequence[Union[type, properties.Property]]] - self.meta = None # type: Optional[meta.Meta] - - def __call__(self): - # type: (...) -> Any - """ - Return `self.data` unmarshalled - """ - - unmarshalled_data = self.data - - if ( - (self.data is not None) and - (self.data is not properties.NULL) - ): - # If the data is a serial `Model`, get it's metadata - if isinstance(self.data, abc.model.Model): - - self.meta = meta.read(self.data) - - if self.meta is None: # Only un-marshall models if they have no metadata yet (are generic) - - # If `types` is a function, it should be one which expects to receive marshalled data and returns a list - # of types which are applicable - if callable(self.types): - self.types = self.types(self.data) - - # If the data provided is a `Generator`, make it static by casting the data into a tuple - if isinstance(self.data, Generator): - self.data = tuple(self.data) - - if self.types is None: - - # If no types are provided, we unmarshal the data into one of serial's generic container types - unmarshalled_data = self.as_container_or_simple_type - - else: - - self.backport_types() - - unmarshalled_data = None - successfully_unmarshalled = False - - first_error = None # type: Optional[Exception] - - # Attempt to un-marshal the data as each type, in the order provided - for type_ in self.types: - - error = None # type: Optional[Union[AttributeError, KeyError, TypeError, ValueError]] - - try: - - unmarshalled_data = self.as_type(type_) - - # if (self.data is not None) and (unmarshalled_data is None): - # raise RuntimeError(self.data) - - # If the data is un-marshalled successfully, we do not need to try any further types - successfully_unmarshalled = True - break - - except (AttributeError, KeyError, TypeError, ValueError) as e: - - error = e - - if (first_error is None) and (error is not None): - first_error = error - - if not successfully_unmarshalled: - - if (first_error is None) or isinstance(first_error, TypeError): - - raise errors.UnmarshalTypeError( - self.data, - types=self.types, - value_types=self.value_types, - item_types=self.item_types - ) - - elif isinstance(first_error, ValueError): - - raise errors.UnmarshalValueError( - self.data, - types=self.types, - value_types=self.value_types, - item_types=self.item_types - ) - - else: - - raise first_error # noqa - pylint erroneously identifies this as raising `None` - - return unmarshalled_data - - @property - def as_container_or_simple_type(self): - # type: (...) -> Any - - """ - This function unmarshalls and returns the data into one of serial's container types, or if the data is of a - simple data type--it returns that data unmodified - """ - - unmarshalled_data = self.data - - if isinstance(self.data, abc.model.Dictionary): - - type_ = type(self.data()) - - if self.value_types is not None: - unmarshalled_data = type_(self.data, value_types=self.value_types) - - elif isinstance(self.data, abc.model.Array): - - if self.item_types is not None: - unmarshalled_data = serial.model.Array(self.data, item_types=self.item_types) - - elif isinstance(self.data, (dict, collections.OrderedDict)): - - unmarshalled_data = serial.model.Dictionary(self.data, value_types=self.value_types) - - elif ( - isinstance(self.data, (collections_abc.Set, collections_abc.Sequence)) - ) and ( - not isinstance(self.data, (str, bytes, native_str)) - ): - - unmarshalled_data = serial.model.Array(self.data, item_types=self.item_types) - - elif not isinstance(self.data, (str, bytes, native_str, Number, Decimal, date, datetime, bool, abc.model.Model)): - - raise errors.UnmarshalValueError( - '%s cannot be un-marshalled' % repr(self.data) - ) - - return unmarshalled_data - - def backport_types(self): - # type: (...) -> None - """ - This examines a set of types passed to `unmarshal`, and resolves any compatibility issues with the python - version being utilized - """ - - if (str in self.types) and (native_str is not str) and (native_str not in self.types): - - self.types = tuple(chain(*( - ((type_, native_str) if (type_ is str) else (type_,)) - for type_ in self.types - ))) # type: Tuple[Union[type, properties.Property], ...] - - def as_type( - self, - type_, # type: Union[type, properties.Property] - ): - # type: (...) -> bool - - unmarshalled_data = None # type: Union[abc.model.Model, Number, str, bytes, date, datetime] - - if isinstance( - type_, - properties.Property - ): - - unmarshalled_data = type_.unmarshal(self.data) - - elif isinstance(type_, type): - - if isinstance( - self.data, - (dict, collections.OrderedDict, abc.model.Model) - ): - - if issubclass(type_, abc.model.Object): - - unmarshalled_data = type_(self.data) - - elif issubclass( - type_, - abc.model.Dictionary - ): - - unmarshalled_data = type_(self.data, value_types=self.value_types) - - elif issubclass( - type_, - (dict, collections.OrderedDict) - ): - - unmarshalled_data = serial.model.Dictionary(self.data, value_types=self.value_types) - - else: - - raise TypeError(self.data) - - elif ( - isinstance(self.data, (collections_abc.Set, collections_abc.Sequence, abc.model.Array)) and - (not isinstance(self.data, (str, bytes, native_str))) - ): - - if issubclass(type_, abc.model.Array): - - unmarshalled_data = type_(self.data, item_types=self.item_types) - - elif issubclass( - type_, - (collections_abc.Set, collections_abc.Sequence) - ) and not issubclass( - type_, - (str, bytes, native_str) - ): - - unmarshalled_data = serial.model.Array(self.data, item_types=self.item_types) - - else: - - raise TypeError('%s is not of type `%s`' % (repr(self.data), repr(type_))) - - elif isinstance(self.data, type_): - - if isinstance(self.data, Decimal): - unmarshalled_data = float(self.data) - else: - unmarshalled_data = self.data - - else: - - raise TypeError(self.data) - - return unmarshalled_data - - -def unmarshal( - data, # type: Any - types=None, # type: Optional[Union[Sequence[Union[type, properties.Property]], type, properties.Property]] - value_types=None, # type: Optional[Union[Sequence[Union[type, properties.Property]], type, properties.Property]] - item_types=None, # type: Optional[Union[Sequence[Union[type, properties.Property]], type, properties.Property]] -): - # type: (...) -> Optional[Union[abc.model., str, Number, date, datetime]] - """ - Converts `data` into an instance of a serial model, and recursively does the same for all member data. - - Parameters: - - - data ([type|serial.properties.Property]): One or more data types. Each type - - This is done by attempting to cast that data into a series of `types`. - - to "un-marshal" data which has been deserialized from bytes or text, but is still represented - by generic containers - """ - - unmarshalled_data = _Unmarshal( - data, - types=types, - value_types=value_types, - item_types=item_types - )() - - return unmarshalled_data - - -def serialize(data, format_='json'): - # type: (Union[abc.model.Model, str, Number], Optional[str]) -> str - """ - Serializes instances of `serial.model.Object` as JSON or YAML. - """ - instance_hooks = None - - if isinstance(data, abc.model.Model): - - instance_hooks = hooks.read(data) - - if (instance_hooks is not None) and (instance_hooks.before_serialize is not None): - data = instance_hooks.before_serialize(data) - - if format_ not in ('json', 'yaml'): # , 'xml' - - format_ = format_.lower() - - if format_ not in ('json', 'yaml'): - - raise ValueError( - 'Supported `serial.model.serialize()` `format_` values include "json" and "yaml" (not "%s").' % - format_ - ) - - if format_ == 'json': - data = json.dumps(marshal(data)) - elif format_ == 'yaml': - data = yaml.dump(marshal(data)) - - if (instance_hooks is not None) and (instance_hooks.after_serialize is not None): - data = instance_hooks.after_serialize(data) - - if not isinstance(data, str): - if isinstance(data, native_str): - data = str(data) - - return data - - -def deserialize(data, format_): - # type: (Optional[Union[str, IOBase, addbase]], str) -> Any - """ - Parameters: - - - data (str|io.IOBase|io.addbase): - - This can be a string or file-like object containing JSON, YAML, or XML serialized inforation. - - - format_ (str): - - This can be "json", "yaml" or "xml". - - Returns: - - A deserialized representation of the information you provided. - """ - if format_ not in ('json', 'yaml'): # , 'xml' - raise NotImplementedError( - 'Deserialization of data in the format %s is not currently supported.' % repr(format_) - ) - if not isinstance(data, (str, bytes)): - data = read(data) - if isinstance(data, bytes): - data = str(data, encoding='utf-8') - if isinstance(data, str): - if format_ == 'json': - data = json.loads( - data, - object_hook=collections.OrderedDict, - object_pairs_hook=collections.OrderedDict - ) - elif format_ == 'yaml': - data = yaml.load(data) - return data - - -def detect_format(data): - # type: (Optional[Union[str, IOBase, addbase]]) -> Tuple[Any, str] - """ - Parameters: - - - data (str|io.IOBase|io.addbase): - - This can be a string or file-like object containing JSON, YAML, or XML serialized inforation. - - Returns: - - A tuple containing the deserialized information and a string indicating the format of that information. - """ - if not isinstance(data, str): - try: - data = utilities.read(data) - except TypeError: - return data, None - formats = ('json', 'yaml') # , 'xml' - format_ = None - for potential_format in formats: - try: - data = deserialize(data, potential_format) - format_ = potential_format - break - except (ValueError, yaml.YAMLError): - pass - if format is None: - raise ValueError( - 'The data provided could not be parsed:\n' + repr(data) - ) - return data, format_ - - -def validate( - data, # type: Optional[abc.model.Model] - types=None, # type: Optional[Union[type, properties.Property, model.Object, Callable]] - raise_errors=True # type: bool -): - # type: (...) -> Sequence[str] - """ - This function verifies that all properties/items/values in an instance of `serial.abc.model.Model` are of the - correct data type(s), and that all required attributes are present (if applicable). If `raise_errors` is `True` - (this is the default)--violations will result in a validation error. If `raise_errors` is `False`--a list of error - messages will be returned if invalid/missing information is found, or an empty list otherwise. - """ - - if isinstance(data, Generator): - data = tuple(data) - - error_messages = [] - - error_message = None - - if types is not None: - - if callable(types): - types = types(data) - - if (str in types) and (native_str is not str) and (native_str not in types): - - types = tuple(chain(*( - ((type_, native_str) if (type_ is str) else (type_,)) - for type_ in types - ))) - - valid = False - - for type_ in types: - - if isinstance(type_, type) and isinstance(data, type_): - - valid = True - break - - elif isinstance(type_, properties.Property): - - if type_.types is None: - - valid = True - break - - try: - - validate(data, type_.types, raise_errors=True) - valid = True - break - - except errors.ValidationError: - - pass - - if not valid: - - error_message = ( - 'Invalid data:\n\n%s\n\nThe data must be one of the following types:\n\n%s' % ( - '\n'.join( - ' ' + line - for line in repr(data).split('\n') - ), - '\n'.join(chain( - (' (',), - ( - ' %s,' % '\n'.join( - ' ' + line - for line in repr(type_).split('\n') - ).strip() - for type_ in types - ), - (' )',) - )) - ) - ) - - if error_message is not None: - - if (not error_messages) or (error_message not in error_messages): - - error_messages.append(error_message) - - if ('_validate' in dir(data)) and callable(data._validate): - - error_messages.extend( - error_message for error_message in - data._validate(raise_errors=False) - if error_message not in error_messages - ) - - if raise_errors and error_messages: - raise errors.ValidationError('\n' + '\n\n'.join(error_messages)) - - return error_messages diff --git a/backend/venv/lib/python3.6/site-packages/serial/meta.py b/backend/venv/lib/python3.6/site-packages/serial/meta.py deleted file mode 100644 index 51e1ebd..0000000 --- a/backend/venv/lib/python3.6/site-packages/serial/meta.py +++ /dev/null @@ -1,812 +0,0 @@ -from __future__ import nested_scopes, generators, division, absolute_import, with_statement, \ - print_function, unicode_literals -from .utilities.compatibility import backport - -backport() # noqa - -from future.utils import native_str - -import numbers -import operator -import re -import collections -from collections import OrderedDict -from copy import copy, deepcopy -from itertools import chain -from numbers import Number - -try: - from typing import Optional, Dict, Sequence, Tuple, Mapping, Union, Any, List -except ImportError: - Optional = Sequence = Dict = Tuple = Mapping = Union = Any = List = None - -import serial -from serial.utilities import qualified_name, properties_values, collections_abc -from serial.abc.model import Model -from serial.abc.properties import Property - -_DOT_SYNTAX_RE = re.compile( - r'^\d+(\.\d+)*$' -) - - -class Meta(object): - - def __copy__(self): - new_instance = self.__class__() - for a in dir(self): - if a[0] != '_': - v = getattr(self, a) - if not isinstance(v, collections.Callable): - setattr(new_instance, a, v) - return new_instance - - def __deepcopy__(self, memo=None): - # type: (Optional[dict]) -> Meta - new_instance = self.__class__() - for a, v in properties_values(self): - setattr(new_instance, a, deepcopy(v, memo=memo)) - return new_instance - - def __bool__(self): - return True - - def __repr__(self): - return ('\n'.join( - ['%s(' % qualified_name(type(self))] + - [ - ' %s=%s,' % (p, repr(v)) - for p, v in properties_values(self) - ] + - [')'] - )) - - -class Version(Meta): - - def __init__( - self, - version_number=None, # type: Optional[str] - specification=None, # type: Optional[Sequence[str]] - equals=None, # type: Optional[Sequence[Union[str, Number]]] - not_equals=None, # type: Optional[Sequence[Union[str, Number]]] - less_than=None, # type: Optional[Sequence[Union[str, Number]]] - less_than_or_equal_to=None, # type: Optional[Sequence[Union[str, Number]]] - greater_than=None, # type: Optional[Sequence[Union[str, Number]]] - greater_than_or_equal_to=None, # type: Optional[Sequence[Union[str, Number]]] - ): - if isinstance(version_number, str) and ( - (specification is None) and - (equals is None) and - (not_equals is None) and - (less_than is None) and - (less_than_or_equal_to is None) and - (greater_than is None) and - (greater_than_or_equal_to is None) - ): - specification = None - for s in version_number.split('&'): - if '==' in s: - s, equals = s.split('==') - elif '<=' in s: - s, less_than_or_equal_to = s.split('<=') - elif '>=' in s: - s, greater_than_or_equal_to = s.split('>=') - elif '<' in s: - s, less_than = s.split('<') - elif '>' in s: - s, greater_than = s.split('>') - elif '!=' in s: - s, not_equals = s.split('!=') - elif '=' in s: - s, equals = s.split('=') - if specification: - if s != specification: - raise ValueError( - 'Multiple specifications cannot be associated with an instance of ' + - '`serial.meta.Version`: ' + repr(version_number) - ) - elif s: - specification = s - self.specification = specification - self.equals = equals - self.not_equals = not_equals - self.less_than = less_than - self.less_than_or_equal_to = less_than_or_equal_to - self.greater_than = greater_than - self.greater_than_or_equal_to = greater_than_or_equal_to - - def __eq__(self, other): - # type: (Any) -> bool - - compare_properties_functions = ( - ('equals', operator.eq), - ('not_equals', operator.ne), - ('less_than', operator.lt), - ('less_than_or_equal_to', operator.le), - ('greater_than', operator.gt), - ('greater_than_or_equal_to', operator.ge), - ) - - if ( - (isinstance(other, str) and _DOT_SYNTAX_RE.match(other)) or - isinstance(other, (collections_abc.Sequence, int)) - ): - if isinstance(other, (native_str, bytes, numbers.Number)): - other = str(other) - if isinstance(other, str): - - other = other.rstrip('.0') - if other == '': - other_components = (0,) - else: - other_components = tuple(int(other_component) for other_component in other.split('.')) - else: - other_components = tuple(other) - for compare_property, compare_function in compare_properties_functions: - compare_value = getattr(self, compare_property) - if compare_value is not None: - compare_values = tuple(int(n) for n in compare_value.split('.')) - other_values = copy(other_components) - ld = len(other_values) - len(compare_values) - if ld < 0: - other_values = tuple(chain(other_values, [0] * (-ld))) - elif ld > 0: - compare_values = tuple(chain(compare_values, [0] * ld)) - if not compare_function(other_values, compare_values): - return False - else: - for compare_property, compare_function in compare_properties_functions: - compare_value = getattr(self, compare_property) - if (compare_value is not None) and not compare_function(other, compare_value): - return False - return True - - def __str__(self): - representation = [] - for property, operator in ( - ('equals', '=='), - ('not_equals', '!='), - ('greater_than', '>'), - ('greater_than_or_equal_to', '>='), - ('less_than', '<'), - ('less_than_or_equal_to', '<='), - ): - v = getattr(self, property) - if v is not None: - representation.append( - self.specification + operator + v - ) - return '&'.join(representation) - - -class Object(Meta): - - def __init__( - self, - properties=None, # type: Optional[Properties] - ): - self._properties = None # type: Optional[Properties] - self.properties = properties - - @property - def properties(self): - # type: () -> Optional[Properties] - return self._properties - - @properties.setter - def properties( - self, - properties_ - # type: Optional[Union[Dict[str, Property], Sequence[Tuple[str, Property]]]] - ): - # type: (...) -> None - self._properties = Properties(properties_) - - -class Dictionary(Meta): - - def __init__( - self, - value_types=None, # type: Optional[Sequence[Property, type]] - ): - self._value_types = None # type: Optional[Tuple] - self.value_types = value_types - - @property - def value_types(self): - # type: () -> Optional[Dict[str, Union[type, Property, serial.model.Object]]] - return self._value_types - - @value_types.setter - def value_types(self, value_types): - # type: (Optional[Sequence[Union[type, Property, serial.model.Object]]]) -> None - - if value_types is not None: - - if isinstance(value_types, (type, Property)): - value_types = (value_types,) - - if native_str is not str: - - if isinstance(value_types, collections.Callable): - - _types = value_types - - def value_types(d): - # type: (Any) -> Any - - ts = _types(d) - - if (ts is not None) and (str in ts) and (native_str not in ts): - ts = tuple(chain(*( - ((t, native_str) if (t is str) else (t,)) - for t in ts - ))) - - return ts - - elif (str in value_types) and (native_str is not str) and (native_str not in value_types): - - value_types = chain(*( - ((t, native_str) if (t is str) else (t,)) - for t in value_types - )) - - if not isinstance(value_types, collections_abc.Callable): - value_types = tuple(value_types) - - self._value_types = value_types - - -class Array(Meta): - - def __init__( - self, - item_types=None, # type: Optional[Sequence[Property, type]] - ): - self._item_types = None # type: Optional[Tuple] - self.item_types = item_types - - @property - def item_types(self): - return self._item_types - - @item_types.setter - def item_types(self, item_types): - # type: (Optional[Sequence[Union[type, Property, serial.model.Object]]]) -> None - if item_types is not None: - if isinstance(item_types, (type, Property)): - item_types = (item_types,) - if native_str is not str: - if isinstance(item_types, collections.Callable): - _types = item_types - - def item_types(d): - # type: (Any) -> Any - ts = _types(d) - if (ts is not None) and (str in ts) and (native_str not in ts): - ts = tuple(chain(*( - ((t, native_str) if (t is str) else (t,)) - for t in ts - ))) - return ts - elif (str in item_types) and (native_str is not str) and (native_str not in item_types): - item_types = chain(*( - ((t, native_str) if (t is str) else (t,)) - for t in item_types - )) - if not callable(item_types): - item_types = tuple(item_types) - self._item_types = item_types - - -class Properties(OrderedDict): - - def __init__( - self, - items=( - None - ) # type: Optional[Union[Dict[str, Property], List[Tuple[str, Property]]]] - ): - if items is None: - super().__init__() - else: - if isinstance(items, OrderedDict): - items = items.items() - elif isinstance(items, dict): - items = sorted(items.items()) - super().__init__(items) - - def __setitem__(self, key, value): - # type: (str, Property) -> None - if not isinstance(value, Property): - raise ValueError(value) - super().__setitem__(key, value) - - def __copy__(self): - # type: () -> Properties - return self.__class__(self) - - def __deepcopy__(self, memo=None): - # type: (dict) -> Properties - return self.__class__( - tuple( - (k, deepcopy(v, memo=memo)) - for k, v in self.items() - ) - ) - - def __repr__(self): - representation = [ - qualified_name(type(self)) + '(' - ] - items = tuple(self.items()) - if len(items) > 0: - representation[0] += '[' - for k, v in items: - rv = ( - qualified_name(v) if isinstance(v, type) else - repr(v) - ) - rvls = rv.split('\n') - if len(rvls) > 1: - rvs = [rvls[0]] - for rvl in rvls[1:]: - rvs.append(' ' + rvl) - rv = '\n'.join(rvs) - representation += [ - ' (', - ' %s,' % repr(k), - ' %s' % rv, - ' ),' - ] - else: - representation.append( - ' (%s, %s),' % (repr(k), rv) - ) - representation[-1] = representation[-1][:-1] - representation.append( - ']' - ) - representation[-1] += ')' - if len(representation) > 2: - return '\n'.join(representation) - else: - return ''.join(representation) - - -def read( - model # type: Union[type, serial.abc.model.Model] -): - # type: (...) -> Optional[Meta] - - if isinstance( - model, - Model - ): - - return model._meta or read(type(model)) - - elif isinstance(model, type) and issubclass(model, Model): - - return model._meta - - else: - - try: - - repr_model = repr(model) - - except: - - repr_model = object.__repr__(model) - - raise TypeError( - '%s requires a parameter which is an instance or sub-class of `%s`, not%s' % ( - serial.utilities.calling_function_qualified_name(), - qualified_name(Model), - ( - ':\n' + repr_model - if '\n' in repr_model else - ' `%s`' % repr_model - ) - ) - ) - - -def writable( - model # type: Union[type, serial.abc.model.Model] -): - # type: (...) -> Optional[Meta] - if isinstance(model, Model): - if model._meta is None: - model._meta = deepcopy(writable(type(model))) - elif isinstance(model, type) and issubclass(model, Model): - if model._meta is None: - model._meta = ( - Object() - if issubclass(model, serial.model.Object) else - Array() - if issubclass(model, serial.model.Array) else - Dictionary() - if issubclass(model, serial.model.Dictionary) - else None - ) - else: - for b in model.__bases__: - if hasattr(b, '_meta') and (model._meta is b._meta): - model._meta = deepcopy(model._meta) - break - else: - repr_model = repr(model) - raise TypeError( - '%s requires a parameter which is an instance or sub-class of `%s`, not%s' % ( - serial.utilities.calling_function_qualified_name(), - qualified_name(Model), - ( - ':\n' + repr_model - if '\n' in repr_model else - ' `%s`' % repr_model - ) - ) - ) - return model._meta - - -def write( - model, # type: Union[type, serial.model.Object] - meta # type: Meta -): - # type: (...) -> None - if isinstance(model, Model): - model_type = type(model) - elif isinstance(model, type) and issubclass(model, Model): - model_type = model - else: - repr_model = repr(model) - raise TypeError( - '%s requires a value for the parameter `model` which is an instance or sub-class of `%s`, not%s' % ( - serial.utilities.calling_function_qualified_name(), - qualified_name(Model), - ( - ':\n' + repr_model - if '\n' in repr_model else - ' `%s`' % repr_model - ) - ) - ) - metadata_type = ( - Object - if issubclass(model_type, serial.model.Object) else - Array - if issubclass(model_type, serial.model.Array) else - Dictionary - if issubclass(model_type, serial.model.Dictionary) - else None - ) - if not isinstance(meta, metadata_type): - raise ValueError( - 'Metadata assigned to `%s` must be of type `%s`' % ( - qualified_name(model_type), - qualified_name(metadata_type) - ) - ) - model._meta = meta - - -_UNIDENTIFIED = None - - -def xpath(model, xpath_=_UNIDENTIFIED): - # type: (serial.abc.model.Model, Optional[str]) -> Optional[str] - """ - Return the xpath at which the element represented by this object was found, relative to the root document. If - the parameter `xpath_` is provided--set the value - """ - - if not isinstance(model, Model): - - raise TypeError( - '`model` must be an instance of `%s`, not %s.' % (qualified_name(Model), repr(model)) - ) - - if xpath_ is not _UNIDENTIFIED: - - if not isinstance(xpath_, str): - - if isinstance(xpath_, native_str): - xpath_ = str(xpath_) - else: - raise TypeError( - '`xpath_` must be a `str`, not %s.' % repr(xpath_) - ) - - model._xpath = xpath_ - - if isinstance(model, serial.model.Dictionary): - for k, v in model.items(): - if isinstance(v, Model): - xpath(v, '%s/%s' % (xpath_, k)) - elif isinstance(model, serial.model.Object): - for pn, p in read(model).properties.items(): - k = p.name or pn - v = getattr(model, pn) - if isinstance(v, Model): - xpath(v, '%s/%s' % (xpath_, k)) - elif isinstance(model, serial.model.Array): - for i in range(len(model)): - v = model[i] - if isinstance(v, Model): - xpath(v, '%s[%s]' % (xpath_, str(i))) - return model._xpath - - -def pointer(model, pointer_=_UNIDENTIFIED): - # type: (serial.abc.model.Model, Optional[str]) -> Optional[str] - - if not isinstance(model, Model): - raise TypeError( - '`model` must be an instance of `%s`, not %s.' % (qualified_name(Model), repr(model)) - ) - - if pointer_ is not _UNIDENTIFIED: - - if not isinstance(pointer_, (str, native_str)): - raise TypeError( - '`pointer_` must be a `str`, not %s (of type `%s`).' % (repr(pointer_), type(pointer_).__name__) - ) - - model._pointer = pointer_ - - if isinstance(model, serial.model.Dictionary): - - for k, v in model.items(): - if isinstance(v, Model): - pointer(v, '%s/%s' % (pointer_, k.replace('~', '~0').replace('/', '~1'))) - - elif isinstance(model, serial.model.Object): - - for pn, property in read(model).properties.items(): - - k = property.name or pn - v = getattr(model, pn) - - if isinstance(v, Model): - pointer(v, '%s/%s' % (pointer_, k.replace('~', '~0').replace('/', '~1'))) - - elif isinstance(model, serial.model.Array): - - for i in range(len(model)): - - v = model[i] - - if isinstance(v, Model): - pointer(v, '%s[%s]' % (pointer_, str(i))) - - return model._pointer - - -def url(model, url_=_UNIDENTIFIED): - # type: (serial.abc.model.Model, Optional[str]) -> Optional[str] - if not isinstance(model, serial.abc.model.Model): - raise TypeError( - '`model` must be an instance of `%s`, not %s.' % (qualified_name(Model), repr(model)) - ) - if url_ is not _UNIDENTIFIED: - if not isinstance(url_, str): - raise TypeError( - '`url_` must be a `str`, not %s.' % repr(url_) - ) - model._url = url_ - if isinstance(model, serial.model.Dictionary): - for v in model.values(): - if isinstance(v, Model): - url(v, url_) - elif isinstance(model, serial.model.Object): - for pn in read(model).properties.keys(): - v = getattr(model, pn) - if isinstance(v, Model): - url(v, url_) - elif isinstance(model, serial.model.Array): - for v in model: - if isinstance(v, Model): - url(v, url_) - return model._url - - -def format_(model, serialization_format=_UNIDENTIFIED): - - # type: (serial.abc.model.Model, Optional[str]) -> Optional[str] - if not isinstance(model, Model): - - raise TypeError( - '`model` must be an instance of `%s`, not %s.' % (qualified_name(Model), repr(model)) - ) - - if serialization_format is not _UNIDENTIFIED: - - if not isinstance(serialization_format, str): - - if isinstance(serialization_format, native_str): - serialization_format = str(serialization_format) - else: - raise TypeError( - '`serialization_format` must be a `str`, not %s.' % repr(serialization_format) - ) - - model._format = serialization_format - - if isinstance(model, serial.model.Dictionary): - for v in model.values(): - if isinstance(v, Model): - format_(v, serialization_format) - elif isinstance(model, serial.model.Object): - for pn in read(model).properties.keys(): - v = getattr(model, pn) - if isinstance(v, Model): - format_(v, serialization_format) - elif isinstance(model, serial.model.Array): - for v in model: - if isinstance(v, Model): - format_(v, serialization_format) - - return model._format - - -def version(data, specification, version_number): - # type: (serial.abc.model.Model, str, Union[str, int, Sequence[int]]) -> None - """ - Recursively alters model class or instance metadata based on version number metadata associated with an - object's properties. This allows one data model to represent multiple versions of a specification and dynamically - change based on the version of a specification represented. - - Arguments: - - - data (serial.abc.model.Model) - - - specification (str): - - The specification to which the `version_number` argument applies. - - - version_number (str|int|[int]): - - A version number represented as text (in the form of integers separated by periods), an integer, or a - sequence of integers. - """ - if not isinstance(data, serial.abc.model.Model): - raise TypeError( - 'The data provided is not an instance of serial.abc.model.Model: ' + repr(data) - ) - - def version_match(property_): - # type: (serial.properties.Property) -> bool - if property_.versions is not None: - version_matched = False - specification_matched = False - for applicable_version in property_.versions: - if applicable_version.specification == specification: - specification_matched = True - if applicable_version == version_number: - version_matched = True - break - if specification_matched and (not version_matched): - return False - return True - - def version_properties(properties_): - # type: (Sequence[serial.properties.Property]) -> Optional[Sequence[Meta]] - changed = False - nps = [] - - for property in properties_: - - if isinstance(property, serial.properties.Property): - if version_match(property): - np = version_property(property) - if np is not property: - changed = True - nps.append(np) - else: - changed = True - else: - nps.append(property) - if changed: - return tuple(nps) - else: - return None - - def version_property(property): - # type: (serial.properties.Property) -> Meta - changed = False - if isinstance(property, serial.properties.Array) and (property.item_types is not None): - item_types = version_properties(property.item_types) - if item_types is not None: - if not changed: - property = deepcopy(property) - property.item_types = item_types - changed = True - elif isinstance(property, serial.properties.Dictionary) and (property.value_types is not None): - value_types = version_properties(property.value_types) - if value_types is not None: - if not changed: - property = deepcopy(property) - property.value_types = value_types - changed = True - if property.types is not None: - types = version_properties(property.types) - if types is not None: - if not changed: - property = deepcopy(property) - property.types = types - return property - - instance_meta = read(data) - class_meta = read(type(data)) - - if isinstance(data, serial.abc.model.Object): - - for property_name in tuple(instance_meta.properties.keys()): - - property = instance_meta.properties[property_name] - - if version_match(property): - np = version_property(property) - if np is not property: - if instance_meta is class_meta: - instance_meta = writable(data) - instance_meta.properties[property_name] = np - else: - if instance_meta is class_meta: - instance_meta = writable(data) - del instance_meta.properties[property_name] - version_ = getattr(data, property_name) - if version_ is not None: - raise serial.errors.VersionError( - '%s - the property `%s` is not applicable in %s version %s:\n%s' % ( - qualified_name(type(data)), - property_name, - specification, - version_number, - str(data) - ) - ) - - value = getattr(data, property_name) - - if isinstance(value, serial.abc.model.Model): - version(value, specification, version_number) - - elif isinstance(data, serial.abc.model.Dictionary): - - if instance_meta and instance_meta.value_types: - - new_value_types = version_properties(instance_meta.value_types) - - if new_value_types: - - if instance_meta is class_meta: - instance_meta = writable(data) - - instance_meta.value_types = new_value_types - - for value in data.values(): - if isinstance(value, serial.abc.model.Model): - version(value, specification, version_number) - - elif isinstance(data, serial.abc.model.Array): - - if instance_meta and instance_meta.item_types: - - new_item_types = version_properties(instance_meta.item_types) - - if new_item_types: - - if instance_meta is class_meta: - instance_meta = writable(data) - - instance_meta.item_types = new_item_types - - for item in data: - if isinstance(item, serial.abc.model.Model): - version(item, specification, version_number) diff --git a/backend/venv/lib/python3.6/site-packages/serial/model.py b/backend/venv/lib/python3.6/site-packages/serial/model.py deleted file mode 100644 index 4b7e8e1..0000000 --- a/backend/venv/lib/python3.6/site-packages/serial/model.py +++ /dev/null @@ -1,1306 +0,0 @@ -""" -This module defines the building blocks of a `serial` based data model. -""" - -# Tell the linters what's up: -# pylint:disable=wrong-import-position,consider-using-enumerate,useless-object-inheritance -# mccabe:options:max-complexity=999 -from __future__ import nested_scopes, generators, division, absolute_import, with_statement, \ - print_function, unicode_literals - -from .utilities.compatibility import backport, BACKWARDS_COMPATIBILITY_IMPORTS - -backport() # noqa - -from future.utils import native_str - -# region Built-In Imports - -import re -import sys - -from urllib.parse import urljoin - -from copy import deepcopy -from io import IOBase -from itertools import chain -from numbers import Number - -# endregion - -# region 3rd-Party Maintained Package Imports - -# endregion - -# region Serial Imports - -from .utilities import qualified_name, collections, Generator -from . import properties, meta, errors, hooks, abc -from .marshal import marshal, unmarshal, serialize, detect_format, validate, UNMARSHALLABLE_TYPES - - -# endregion - - -# region Compatibility Conditionals - -# The following detects the presence of the typing library, and utilizes typing classes if possible. -# All typing classes in this package are referenced in a backwards-compatible fashion, so if this library -# is not present, the package will still function. - -try: - from typing import Union, Dict, Any, AnyStr, IO, Sequence, Mapping, Callable, Tuple, Optional, Set # noqa -except ImportError: - Union = Dict = Any = AnyStr = IO = Sequence = Mapping = Callable = Tuple = Optional = Set = None - -# endregion - - -class Object(object): - - _format = None # type: Optional[str] - _meta = None # type: Optional[meta.Object] - _hooks = None # type: Optional[hooks.Object] - - def __init__( - self, - _=None, # type: Optional[Union[str, bytes, dict, Sequence, IO]] - ): - - self._meta = None # type: Optional[meta.Object] - self._hooks = None # type: Optional[hooks.Object] - self._url = None # type: Optional[str] - self._xpath = None # type: Optional[str] - self._pointer = None # type: Optional[str] - - url = None - - if _ is not None: - - if isinstance(_, Object): - - instance_meta = meta.read(_) - - if meta.read(self) is not instance_meta: - meta.write(self, deepcopy(instance_meta)) - - instance_hooks = hooks.read(_) - - if hooks.read(self) is not instance_hooks: - hooks.write(self, deepcopy(instance_hooks)) - - for property_name in instance_meta.properties.keys(): - - try: - - setattr(self, property_name, getattr(_, property_name)) - - except TypeError as error: - - label = '\n - %s.%s: ' % (qualified_name(type(self)), property_name) - - if error.args: - error.args = tuple( - chain( - (label + error.args[0],), - error.args[1:] - ) - ) - else: - error.args = (label + serialize(_),) - raise error - else: - - if isinstance(_, IOBase): - - if hasattr(_, 'url'): - url = _.url - elif hasattr(_, 'name'): - url = urljoin('file:', _.name) - - _, format_ = detect_format(_) - - if isinstance(_, dict): - - for property_name, value in _.items(): - - if value is None: - value = properties.NULL - - try: - - self[property_name] = value - - except KeyError as error: - - if error.args and len(error.args) == 1: - - error.args = ( - r'%s.%s: %s' % (qualified_name(type(self)), error.args[0], repr(_)), - ) - - raise error - - else: - if format_ is None: - _dir = tuple(property_name for property_name in dir(_) if property_name[0] != '_') - for property_name in meta.writable(self.__class__).properties.keys(): - if property_name in _dir: - setattr(self, getattr(_, property_name)) - else: - raise TypeError( - 'The `_` parameter must be a string, file-like object, or dictionary, not `%s`' % - repr(_) - ) - if format_ is not None: - meta.format_(self, format_) - if url is not None: - meta.url(self, url) - if meta.pointer(self) is None: - meta.pointer(self, '#') - if meta.xpath(self) is None: - meta.xpath(self, '') - - def __hash__(self): - return id(self) - - def __setattr__(self, property_name, value): - # type: (Object, str, Any) -> None - - instance_hooks = None - - unmarshalled_value = value - - if property_name[0] != '_': - - instance_hooks = hooks.read(self) # type: hooks.Object - - if instance_hooks and instance_hooks.before_setattr: - property_name, value = instance_hooks.before_setattr(self, property_name, value) - - try: - - property_definition = meta.read(self).properties[property_name] - - except KeyError: - - raise KeyError( - '`%s` has no attribute "%s".' % ( - qualified_name(type(self)), - property_name - ) - ) - - if value is not None: - - if isinstance(value, Generator): - value = tuple(value) - - try: - - unmarshalled_value = property_definition.unmarshal(value) - - except (TypeError, ValueError) as error: - - message = '\n - %s.%s: ' % ( - qualified_name(type(self)), - property_name - ) - - if error.args and isinstance(error.args[0], str): - - error.args = tuple( - chain( - (message + error.args[0],), - error.args[1:] - ) - ) - - else: - - error.args = (message + repr(value),) - - raise error - - super().__setattr__(property_name, unmarshalled_value) - - if instance_hooks and instance_hooks.after_setattr: - instance_hooks.after_setattr(self, property_name, value) - - def __setitem__(self, key, value): - # type: (str, Any) -> None - - instance_hooks = hooks.read(self) # type: hooks.Object - - if instance_hooks and instance_hooks.before_setitem: - key, value = instance_hooks.before_setitem(self, key, value) - - instance_meta = meta.read(self) - - if key in instance_meta.properties: - - property_name = key - - else: - - property_name = None - - for potential_property_name, property in instance_meta.properties.items(): - if key == property.name: - property_name = potential_property_name - break - - if property_name is None: - raise KeyError( - '`%s` has no property mapped to the name "%s"' % ( - qualified_name(type(self)), - key - ) - ) - - setattr(self, property_name, value) - - if instance_hooks and instance_hooks.after_setitem: - instance_hooks.after_setitem(self, key, value) - - def __delattr__(self, key): - # type: (str) -> None - - instance_meta = meta.read(self) - - if key in instance_meta.properties: - setattr(self, key, None) - else: - super().__delattr__(key) - - def __getitem__(self, key): - # type: (str, Any) -> None - - instance_meta = meta.read(self) - - if key in instance_meta.properties: - - property_name = key - - else: - - property_definition = None - property_name = None - - for pn, pd in instance_meta.properties.items(): - if key == pd.name: - property_name = pn - property_definition = pd - break - - if property_definition is None: - raise KeyError( - '`%s` has no property mapped to the name "%s"' % ( - qualified_name(type(self)), - key - ) - ) - - return getattr(self, property_name) - - def __copy__(self): - # type: () -> Object - return self.__class__(self) - - def __deepcopy__(self, memo): - # type: (Optional[dict]) -> Object - - new_instance = self.__class__() - - instance_meta = meta.read(self) - class_meta = meta.read(type(self)) - - if instance_meta is class_meta: - meta_ = class_meta # type: meta.Object - else: - meta.write(new_instance, deepcopy(instance_meta, memo)) - meta_ = instance_meta # type: meta.Object - - instance_hooks = hooks.read(self) - class_hooks = hooks.read(type(self)) - - if instance_hooks is not class_hooks: - hooks.write(new_instance, deepcopy(instance_hooks, memo)) - - if meta_ is not None: - - for property_name in meta_.properties.keys(): - - try: - - value = getattr(self, property_name) - - if isinstance(value, Generator): - value = tuple(value) - - if value is not None: - - if not callable(value): - value = deepcopy(value, memo) - - setattr(new_instance, property_name, value) - - except TypeError as error: - - label = '%s.%s: ' % (qualified_name(type(self)), property_name) - - if error.args: - error.args = tuple( - chain( - (label + error.args[0],), - error.args[1:] - ) - ) - else: - error.args = (label + serialize(self),) - - raise error - - return new_instance - - def _marshal(self): - # type: (...) -> collections.OrderedDict - object_ = self - instance_hooks = hooks.read(object_) - if (instance_hooks is not None) and (instance_hooks.before_marshal is not None): - object_ = instance_hooks.before_marshal(object_) - data = collections.OrderedDict() - instance_meta = meta.read(object_) - for property_name, property in instance_meta.properties.items(): - value = getattr(object_, property_name) - if value is not None: - key = property.name or property_name - data[key] = property.marshal(value) - if (instance_hooks is not None) and (instance_hooks.after_marshal is not None): - data = instance_hooks.after_marshal(data) - return data - - def __str__(self): - # type: (...) -> str - return serialize(self) - - def __repr__(self): - # type: (...) -> str - representation = [ - '%s(' % qualified_name(type(self)) - ] - instance_meta = meta.read(self) - for property_name in instance_meta.properties.keys(): - value = getattr(self, property_name) - if value is not None: - repr_value = ( - qualified_name(value) - if isinstance(value, type) else - repr(value) - ) - repr_value_lines = repr_value.split('\n') - if len(repr_value_lines) > 2: - rvs = [repr_value_lines[0]] - for rvl in repr_value_lines[1:]: - rvs.append(' ' + rvl) - repr_value = '\n'.join(rvs) - representation.append( - ' %s=%s,' % (property_name, repr_value) - ) - representation.append(')') - if len(representation) > 2: - return '\n'.join(representation) - else: - return ''.join(representation) - - def __eq__(self, other): - # type: (Any) -> bool - if type(self) is not type(other): - return False - instance_meta = meta.read(self) - om = meta.read(other) - self_properties = set(instance_meta.properties.keys()) - other_properties = set(om.properties.keys()) - if self_properties != other_properties: - return False - for property_name in (self_properties & other_properties): - value = getattr(self, property_name) - ov = getattr(other, property_name) - if value != ov: - return False - return True - - def __ne__(self, other): - # type: (Any) -> bool - return False if self == other else True - - def __iter__(self): - instance_meta = meta.read(self) - for property_name, property in instance_meta.properties.items(): - yield property.name or property_name - - def _validate(self, raise_errors=True): - # type: (bool) -> None - - validation_errors = [] - object_ = self - - instance_hooks = hooks.read(self) - - if (instance_hooks is not None) and (instance_hooks.before_validate is not None): - object_ = instance_hooks.before_validate(object_) - - instance_meta = meta.read(object_) - - for property_name, property in instance_meta.properties.items(): - - value = getattr(object_, property_name) - - if value is None: - - if callable(property.required): - required = property.required(object_) - else: - required = property.required - - if required: - - validation_errors.append( - 'The property `%s` is required for `%s`:\n%s' % ( - property_name, - qualified_name(type(object_)), - str(object_) - ) - ) - else: - - if value is properties.NULL: - - types = property.types - - if callable(types): - types = types(value) - - if types is not None: - - if (str in types) and (native_str is not str) and (native_str not in types): - types = tuple(chain(*( - ((type_, native_str) if (type_ is str) else (type_,)) - for type_ in types - ))) - - if properties.Null not in types: - - validation_errors.append( - 'Null values are not allowed in `%s.%s`, ' % ( - qualified_name(type(object_)), property_name - ) + - 'permitted types include: %s.' % ', '.join( - '`%s`' % qualified_name(type_) for type_ in types - ) - ) - else: - - try: - value_validation_error_messages = validate(value, property.types, raise_errors=False) - - if value_validation_error_messages: - - index = 0 - - for error_message in value_validation_error_messages: - value_validation_error_messages[index] = ( - 'Error encountered ' + - 'while attempting to validate property `%s`:\n\n' % property_name + - error_message - ) - - validation_errors.extend(value_validation_error_messages) - - except errors.ValidationError as error: - - message = '%s.%s:\n' % (qualified_name(type(object_)), property_name) - - if error.args: - error.args = tuple(chain( - (error.args[0] + message,), - error.args[1:] - )) - else: - error.args = ( - message, - ) - - if (instance_hooks is not None) and (instance_hooks.after_validate is not None): - instance_hooks.after_validate(object_) - if raise_errors and validation_errors: - raise errors.ValidationError('\n'.join(validation_errors)) - return validation_errors - - -abc.model.Object.register(Object) - - -class Array(list): - - _format = None # type: Optional[str] - _hooks = None # type: Optional[hooks.Array] - _meta = None # type: Optional[meta.Array] - - def __init__( - self, - items=None, # type: Optional[Union[Sequence, Set]] - item_types=( - None - ), # type: Optional[Union[Sequence[Union[type, properties.Property]], type, properties.Property]] - ): - self._meta = None # type: Optional[meta.Array] - self._hooks = None # type: Optional[hooks.Array] - self._url = None # type: Optional[str] - self._xpath = None # type: Optional[str] - self._pointer = None # type: Optional[str] - url = None - if isinstance(items, IOBase): - if hasattr(items, 'url'): - url = items.url - elif hasattr(items, 'name'): - url = urljoin('file:', items.name) - items, format_ = detect_format(items) - if item_types is None: - if isinstance(items, Array): - m = meta.read(items) - if meta.read(self) is not m: - meta.write(self, deepcopy(m)) - else: - meta.writable(self).item_types = item_types - if items is not None: - for item in items: - self.append(item) - if meta.pointer(self) is None: - meta.pointer(self, '#') - if meta.xpath(self) is None: - meta.xpath(self, '') - if url is not None: - meta.url(self, url) - if format_ is not None: - meta.format_(self, format_) - - def __hash__(self): - return id(self) - - def __setitem__( - self, - index, # type: int - value, # type: Any - ): - instance_hooks = hooks.read(self) # type: hooks.Object - - if instance_hooks and instance_hooks.before_setitem: - index, value = instance_hooks.before_setitem(self, index, value) - - m = meta.read(self) # type: Optional[meta.Array] - - if m is None: - item_types = None - else: - item_types = m.item_types - - value = unmarshal(value, types=item_types) - super().__setitem__(index, value) - - if instance_hooks and instance_hooks.after_setitem: - instance_hooks.after_setitem(self, index, value) - - def append(self, value): - # type: (Any) -> None - if not isinstance(value, UNMARSHALLABLE_TYPES): - raise errors.UnmarshalTypeError(value) - - instance_hooks = hooks.read(self) # type: hooks.Array - - if instance_hooks and instance_hooks.before_append: - value = instance_hooks.before_append(self, value) - - instance_meta = meta.read(self) # type: Optional[meta.Array] - - if instance_meta is None: - item_types = None - else: - item_types = instance_meta.item_types - - value = unmarshal(value, types=item_types) - - super().append(value) - - if instance_hooks and instance_hooks.after_append: - instance_hooks.after_append(self, value) - - def __copy__(self): - # type: () -> Array - return self.__class__(self) - - def __deepcopy__(self, memo=None): - # type: (Optional[dict]) -> Array - new_instance = self.__class__() - im = meta.read(self) - cm = meta.read(type(self)) - if im is not cm: - meta.write(new_instance, deepcopy(im, memo=memo)) - ih = hooks.read(self) - ch = hooks.read(type(self)) - if ih is not ch: - hooks.write(new_instance, deepcopy(ih, memo=memo)) - for i in self: - new_instance.append(deepcopy(i, memo=memo)) - return new_instance - - def _marshal(self): - a = self - h = hooks.read(a) - if (h is not None) and (h.before_marshal is not None): - a = h.before_marshal(a) - m = meta.read(a) - a = tuple( - marshal( - i, - types=None if m is None else m.item_types - ) for i in a - ) - if (h is not None) and (h.after_marshal is not None): - a = h.after_marshal(a) - return a - - def _validate( - self, - raise_errors=True - ): - # type: (bool) -> None - validation_errors = [] - a = self - h = hooks.read(a) - - if (h is not None) and (h.before_validate is not None): - a = h.before_validate(a) - - m = meta.read(a) - - if m.item_types is not None: - - for i in a: - - validation_errors.extend(validate(i, m.item_types, raise_errors=False)) - - if (h is not None) and (h.after_validate is not None): - h.after_validate(a) - - if raise_errors and validation_errors: - raise errors.ValidationError('\n'.join(validation_errors)) - - return validation_errors - - def __repr__(self): - representation = [ - qualified_name(type(self)) + '(' - ] - if len(self) > 0: - representation.append(' [') - for i in self: - ri = ( - qualified_name(i) if isinstance(i, type) else - repr(i) - ) - rils = ri.split('\n') - if len(rils) > 1: - ris = [rils[0]] - ris += [ - ' ' + rvl - for rvl in rils[1:] - ] - ri = '\n'.join(ris) - representation.append( - ' %s,' % ri - ) - im = meta.read(self) - cm = meta.read(type(self)) - m = None if (im is cm) else im # type: Optional[meta.Array] - representation.append( - ' ]' + ('' - if m is None or m.item_types is None - else ',') - ) - im = meta.read(self) - cm = meta.read(type(self)) - if im is not cm: - if im.item_types: - representation.append( - ' item_types=(', - ) - for it in im.item_types: - ri = ( - qualified_name(it) if isinstance(it, type) else - repr(it) - ) - rils = ri.split('\n') - if len(rils) > 2: - ris = [rils[0]] - ris += [ - ' ' + rvl - for rvl in rils[1:-1] - ] - ris.append(' ' + rils[-1]) - ri = '\n'.join(ris) - representation.append(' %s,' % ri) - m = meta.read(self) - if len(m.item_types) > 1: - representation[-1] = representation[-1][:-1] - representation.append(' )') - representation.append(')') - if len(representation) > 2: - return '\n'.join(representation) - else: - return ''.join(representation) - - def __eq__(self, other): - # type: (Any) -> bool - if type(self) is not type(other): - return False - length = len(self) - if length != len(other): - return False - for i in range(length): - if self[i] != other[i]: - return False - return True - - def __ne__(self, other): - # type: (Any) -> bool - if self == other: - return False - else: - return True - - def __str__(self): - return serialize(self) - - -abc.model.Array.register(Array) - - -class Dictionary(collections.OrderedDict): - - _format = None # type: Optional[str] - _hooks = None # type: Optional[hooks.Dictionary] - _meta = None # type: Optional[meta.Dictionary] - - def __init__( - self, - items=None, # type: Optional[Mapping] - value_types=( - None - ), # type: Optional[Union[Sequence[Union[type, properties.Property]], type, properties.Property]] - ): - self._meta = None # type: Optional[meta.Dictionary] - self._hooks = None # type: Optional[hooks.Dictionary] - self._url = None # type: Optional[str] - self._xpath = None # type: Optional[str] - self._pointer = None # type: Optional[str] - - url = None - - if isinstance(items, IOBase): - - if hasattr(items, 'url'): - url = items.url - elif hasattr(items, 'name'): - url = urljoin('file:', items.name) - - items, format_ = detect_format(items) - - if value_types is None: - - if isinstance(items, Dictionary): - - m = meta.read(items) - - if meta.read(self) is not m: - meta.write(self, deepcopy(m)) - else: - - meta.writable(self).value_types = value_types - - if items is None: - - super().__init__() - - else: - - if isinstance(items, (collections.OrderedDict, Dictionary)): - items = items.items() - elif isinstance(items, dict): - items = sorted(items.items(), key=lambda kv: kv) - - super().__init__(items) - - if meta.pointer(self) is None: - meta.pointer(self, '#') - - if meta.xpath(self) is None: - meta.xpath(self, '') - - if url is not None: - meta.url(self, url) - - if format_ is not None: - meta.format_(self, format_) - - def __hash__(self): - return id(self) - - def __setitem__( - self, - key, # type: int - value # type: Any - ): - instance_hooks = hooks.read(self) # type: hooks.Dictionary - - if instance_hooks and instance_hooks.before_setitem: - key, value = instance_hooks.before_setitem(self, key, value) - - instance_meta = meta.read(self) # type: Optional[meta.Dictionary] - - if instance_meta is None: - value_types = None - else: - value_types = instance_meta.value_types - - try: - - unmarshalled_value = unmarshal( - value, - types=value_types - ) - - except TypeError as error: - - message = "\n - %s['%s']: " % ( - qualified_name(type(self)), - key - ) - - if error.args and isinstance(error.args[0], str): - - error.args = tuple( - chain( - (message + error.args[0],), - error.args[1:] - ) - ) - - else: - - error.args = (message + repr(value),) - - raise error - - if value is None: - raise RuntimeError(key) - - super().__setitem__( - key, - unmarshalled_value - ) - - if instance_hooks and instance_hooks.after_setitem: - instance_hooks.after_setitem(self, key, unmarshalled_value) - - def __copy__(self): - # type: (Dictionary) -> Dictionary - new_instance = self.__class__() - im = meta.read(self) - cm = meta.read(type(self)) - if im is not cm: - meta.write(new_instance, im) - ih = hooks.read(self) - ch = hooks.read(type(self)) - if ih is not ch: - hooks.write(new_instance, ih) - for k, v in self.items(): - new_instance[k] = v - return new_instance - - def __deepcopy__(self, memo=None): - # type: (dict) -> Dictionary - new_instance = self.__class__() - im = meta.read(self) - cm = meta.read(type(self)) - if im is not cm: - meta.write(new_instance, deepcopy(im, memo=memo)) - ih = hooks.read(self) - ch = hooks.read(type(self)) - if ih is not ch: - hooks.write(new_instance, deepcopy(ih, memo=memo)) - for k, v in self.items(): - new_instance[k] = deepcopy(v, memo=memo) - return new_instance - - def _marshal(self): - """ - This method marshals an instance of `Dictionary` as built-in type `OrderedDict` which can be serialized into - JSON/YAML/XML. - """ - - # This variable is needed because before-marshal hooks are permitted to return altered *copies* of `self`, so - # prior to marshalling--this variable may no longer point to `self` - data = self # type: Union[Dictionary, collections.OrderedDict] - - # Check for hooks - instance_hooks = hooks.read(data) - - # Execute before-marshal hooks, if applicable - if (instance_hooks is not None) and (instance_hooks.before_marshal is not None): - data = instance_hooks.before_marshal(data) - - # Get the metadata, if any has been assigned - instance_meta = meta.read(data) # type: Optional[meta.Dictionary] - - # Check to see if value types are defined in the metadata - if instance_meta is None: - value_types = None - else: - value_types = instance_meta.value_types - - # Recursively convert the data to generic, serializable, data types - unmarshalled_data = collections.OrderedDict( - [ - ( - k, - marshal(v, types=value_types) - ) for k, v in data.items() - ] - ) # type: collections.OrderedDict - - # Execute after-marshal hooks, if applicable - if (instance_hooks is not None) and (instance_hooks.after_marshal is not None): - unmarshalled_data = instance_hooks.after_marshal(unmarshalled_data) - - return unmarshalled_data - - def _validate(self, raise_errors=True): - # type: (Callable) -> None - """ - Recursively validate - """ - - validation_errors = [] - d = self - h = d._hooks or type(d)._hooks - - if (h is not None) and (h.before_validate is not None): - d = h.before_validate(d) - - m = meta.read(d) # type: Optional[meta.Dictionary] - - if m is None: - value_types = None - else: - value_types = m.value_types - - if value_types is not None: - - for k, v in d.items(): - - value_validation_errors = validate(v, value_types, raise_errors=False)\ - - validation_errors.extend(value_validation_errors) - - if (h is not None) and (h.after_validate is not None): - h.after_validate(d) - - if raise_errors and validation_errors: - raise errors.ValidationError('\n'.join(validation_errors)) - - return validation_errors - - def __repr__(self): - representation = [ - qualified_name(type(self)) + '(' - ] - items = tuple(self.items()) - if len(items) > 0: - representation.append(' [') - for k, v in items: - rv = ( - qualified_name(v) if isinstance(v, type) else - repr(v) - ) - rvls = rv.split('\n') - if len(rvls) > 1: - rvs = [rvls[0]] - for rvl in rvls[1:]: - rvs.append(' ' + rvl) - # rvs.append(' ' + rvs[-1]) - rv = '\n'.join(rvs) - representation += [ - ' (', - ' %s,' % repr(k), - ' %s' % rv, - ' ),' - ] - else: - representation.append( - ' (%s, %s),' % (repr(k), rv) - ) - representation[-1] = representation[-1][:-1] - representation.append( - ' ]' - if self._meta is None or self._meta.value_types is None else - ' ],' - ) - cm = meta.read(type(self)) - im = meta.read(self) - if cm is not im: - if self._meta.value_types: - representation.append( - ' value_types=(', - ) - for vt in im.value_types: - rv = ( - qualified_name(vt) if isinstance(vt, type) else - repr(vt) - ) - rvls = rv.split('\n') - if len(rvls) > 1: - rvs = [rvls[0]] - rvs += [ - ' ' + rvl - for rvl in rvls[1:] - ] - rv = '\n'.join(rvs) - representation.append(' %s,' % rv) - if len(self._meta.value_types) > 1: - representation[-1] = representation[-1][:-1] - representation.append(' )') - representation.append(')') - if len(representation) > 2: - return '\n'.join(representation) - else: - return ''.join(representation) - - def __eq__(self, other): - # type: (Any) -> bool - if type(self) is not type(other): - return False - keys = tuple(self.keys()) - other_keys = tuple(other.keys()) - if keys != other_keys: - return False - for k in keys: - if self[k] != other[k]: - return False - return True - - def __ne__(self, other): - # type: (Any) -> bool - if self == other: - return False - else: - return True - - def __str__(self): - return serialize(self) - - -abc.model.Dictionary.register(Dictionary) - - -def from_meta(name, metadata, module=None, docstring=None): - # type: (meta.Meta, str, Optional[str]) -> type - """ - Constructs an `Object`, `Array`, or `Dictionary` sub-class from an instance of `serial.meta.Meta`. - - Arguments: - - - name (str): The name of the class. - - - class_meta (serial.meta.Meta) - - - module (str): Specify the value for the class definition's `__module__` property. The invoking module will be - used if this is not specified (if possible). - - - docstring (str): A docstring to associate with the class definition. - """ - - def typing_from_property(p): - # type: (properties.Property) -> str - if isinstance(p, type): - if p in ( - Union, Dict, Any, Sequence, IO - ): - type_hint = p.__name__ - else: - type_hint = qualified_name(p) - elif isinstance(p, properties.DateTime): - type_hint = 'datetime' - elif isinstance(p, properties.Date): - type_hint = 'date' - elif isinstance(p, properties.Bytes): - type_hint = 'bytes' - elif isinstance(p, properties.Integer): - type_hint = 'int' - elif isinstance(p, properties.Number): - type_hint = qualified_name(Number) - elif isinstance(p, properties.Boolean): - type_hint = 'bool' - elif isinstance(p, properties.String): - type_hint = 'str' - elif isinstance(p, properties.Array): - item_types = None - if p.item_types: - if len(p.item_types) > 1: - item_types = 'Union[%s]' % ( - ', '.join( - typing_from_property(it) - for it in p.item_types - ) - ) - else: - item_types = typing_from_property(p.item_types[0]) - type_hint = 'Sequence' + ( - '[%s]' % item_types - if item_types else - '' - ) - elif isinstance(p, properties.Dictionary): - value_types = None - if p.value_types: - if len(p.value_types) > 1: - value_types = 'Union[%s]' % ( - ', '.join( - typing_from_property(vt) - for vt in p.value_types - ) - ) - else: - value_types = typing_from_property(p.value_types[0]) - type_hint = ( - 'Dict[str, %s]' % value_types - if value_types else - 'dict' - ) - elif p.types: - if len(p.types) > 1: - type_hint = 'Union[%s]' % ', '.join( - typing_from_property(t) for t in p.types - ) - else: - type_hint = typing_from_property(p.types[0]) - else: - type_hint = 'Any' - return type_hint - if docstring is not None: - if '\t' in docstring: - docstring = docstring.replace('\t', ' ') - lines = docstring.split('\n') - indentation_length = float('inf') - for line in lines: - match = re.match(r'^[ ]+', line) - if match: - indentation_length = min( - indentation_length, - len(match.group()) - ) - else: - indentation_length = 0 - break - wrapped_lines = [] - for line in lines: - line = ' ' + line[indentation_length:] - if len(line) > 120: - indent = re.match(r'^[ ]*', line).group() - li = len(indent) - words = re.split(r'([\w]*[\w,/"\'.;\-?`])', line[li:]) - wrapped_line = '' - for word in words: - if (len(wrapped_line) + len(word) + li) <= 120: - wrapped_line += word - else: - wrapped_lines.append(indent + wrapped_line) - wrapped_line = '' if not word.strip() else word - if wrapped_line: - wrapped_lines.append(indent + wrapped_line) - else: - wrapped_lines.append(line) - docstring = '\n'.join( - [' """'] + - wrapped_lines + - [' """'] - ) - if isinstance(metadata, meta.Dictionary): - out = [ - 'class %s(serial.model.Dictionary):' % name - ] - if docstring is not None: - out.append(docstring) - out.append('\n pass') - elif isinstance(metadata, meta.Array): - out = [ - 'class %s(serial.model.Array):' % name - ] - if docstring is not None: - out.append(docstring) - out.append('\n pass') - elif isinstance(metadata, meta.Object): - out = [ - 'class %s(serial.model.Object):' % name - ] - if docstring is not None: - out.append(docstring) - out += [ - '', - ' def __init__(', - ' self,', - ' _=None, # type: Optional[Union[str, bytes, dict, Sequence, IO]]' - ] - for n, p in metadata.properties.items(): - out.append( - ' %s=None, # type: Optional[%s]' % (n, typing_from_property(p)) - ) - out.append( - ' ):' - ) - for n in metadata.properties.keys(): - out.append( - ' self.%s = %s' % (n, n) - ) - out.append(' super().__init__(_)\n\n') - else: - raise ValueError(metadata) - class_definition = '\n'.join(out) - namespace = dict(__name__='from_meta_%s' % name) - imports = '\n'.join([ - 'import serial', - '', - 'serial.utilities.compatibility.backport()', - '' - 'try:', - ' from typing import Union, Dict, Any, Sequence, IO', - 'except ImportError:', - ' Union = Dict = Any = Sequence = IO = None', - ]) - source = '%s\n\n\n%s' % (imports, class_definition) - exec(source, namespace) - result = namespace[name] - result._source = source - - if module is None: - try: - module = sys._getframe(1).f_globals.get('__name__', '__main__') - except (AttributeError, ValueError): - pass - - if module is not None: - result.__module__ = module - - result._meta = metadata - - return result diff --git a/backend/venv/lib/python3.6/site-packages/serial/properties.py b/backend/venv/lib/python3.6/site-packages/serial/properties.py deleted file mode 100644 index 88999e2..0000000 --- a/backend/venv/lib/python3.6/site-packages/serial/properties.py +++ /dev/null @@ -1,941 +0,0 @@ -# Tell the linters what's up: -# pylint:disable=wrong-import-position,consider-using-enumerate,useless-object-inheritance -# mccabe:options:max-complexity=999 -from __future__ import nested_scopes, generators, division, absolute_import, with_statement, \ - print_function, unicode_literals - -from .utilities.compatibility import backport - -backport() # noqa - -from future.utils import native_str - -import numbers # noqa - -from base64 import b64decode, b64encode # noqa -from copy import deepcopy # noqa -from datetime import date, datetime # noqa - -try: - from typing import Union, Optional, Sequence, Mapping, Set, Sequence, Callable, Dict, Any, Hashable, Collection,\ - Tuple -except ImportError: - Union = Optional = Sequence = Mapping = Set = Sequence = Callable = Dict = Any = Hashable = Collection = Tuple =\ - Iterable = None - -import iso8601 # noqa - -from .utilities import collections, collections_abc, qualified_name, properties_values, parameters_defaults,\ - calling_function_qualified_name - -from serial import abc, errors, meta -import serial - - -NoneType = type(None) - - -NULL = None - - -class Null(object): # noqa - inheriting from object is intentional, as this is needed for python 2x compatibility - """ - Instances of this class represent an *explicit* null value, rather than the absence of a - property/attribute/element, as would be inferred from a value of `None`. - """ - - def __init__(self): - if NULL is not None: - raise errors.DefinitionExistsError( - '%s may only be defined once.' % repr(self) - ) - - def __bool__(self): - # type: (...) -> bool - return False - - def __eq__(self, other): - # type: (Any) -> bool - return id(other) == id(self) - - def __hash__(self): - # type: (...) -> int - return 0 - - def __str__(self): - # type: (...) -> str - return 'null' - - def _marshal(self): - # type: (...) -> None - return None - - def __repr__(self): - # type: (...) -> str - return ( - 'NULL' - if self.__module__ in ('__main__', 'builtins', '__builtin__') else - '%s.NULL' % self.__module__ - ) - - def __copy__(self): - # type: (...) -> Null - return self - - def __deepcopy__(self, memo): - # type: (Dict[Hashable, Any]) -> Null - return self - - -NULL = Null() - - -def _validate_type_or_property(type_or_property): - # type: (Union[type, Property]) -> (Union[type, Property]) - - if not isinstance(type_or_property, (type, Property)): - raise TypeError(type_or_property) - - if not ( - (type_or_property is Null) or - ( - isinstance(type_or_property, type) and - issubclass( - type_or_property, - ( - abc.model.Model, - str, - native_str, - bytes, - numbers.Number, - date, - datetime, - Null, - collections_abc.Iterable, - dict, - collections.OrderedDict, - bool - ) - ) - ) or - isinstance(type_or_property, Property) - ): - raise TypeError(type_or_property) - - return type_or_property - - -class Types(list): - """ - Instances of this class are lists which will only take values which are valid types for describing a property - definition. - """ - - def __init__( - self, - property_, # type: Property - items=None # type: Optional[Union[Sequence[Union[type, Property], Types], type, Property]] - ): - # (...) -> None - if not isinstance(property_, Property): - raise TypeError( - 'The parameter `property` must be a `type`, or an instance of `%s`.' % qualified_name(Property) - ) - - self.property_ = property_ - - if isinstance(items, (type, Property)): - items = (items,) - - if items is None: - super().__init__() - else: - super().__init__(items) - - def __setitem__(self, index, value): - # type: (int, Union[type, Property]) -> None - super().__setitem__(index, _validate_type_or_property(value)) - if value is str and (native_str is not str) and (native_str not in self): - super().append(native_str) - - def append(self, value): - # type: (Union[type, Property]) -> None - super().append(_validate_type_or_property(value)) - if value is str and (native_str is not str) and (native_str not in self): - super().append(native_str) - - def __delitem__(self, index): - # type: (int) -> None - value = self[index] - super().__delitem__(index) - if (value is str) and (native_str in self): - self.remove(native_str) - - def pop(self, index=-1): - # type: (int) -> Union[type, Property] - value = super().pop(index) - if (value is str) and (native_str in self): - self.remove(native_str) - return value - - def __copy__(self): - # type: () -> Types - return self.__class__(self.property_, self) - - def __deepcopy__(self, memo=None): - # type: (dict) -> Types - return self.__class__( - self.property_, - tuple( - deepcopy(v, memo=memo) - for v in self - ) - ) - - def __repr__(self): - - representation = [ - qualified_name(type(self)) + '(' - ] - - if self: - - representation[0] += '[' - - for v in self: - - rv = ( - qualified_name(v) if isinstance(v, type) else - repr(v) - ) - rvls = rv.split('\n') - - if len(rvls) > 1: - - rvs = [rvls[0]] - - for rvl in rvls[1:]: - rvs.append(' ' + rvl) - - rv = '\n'.join(rvs) - representation += [ - ' %s' % rv, - ] - - else: - - representation.append( - ' %s,' % rv - ) - - representation[-1] = representation[-1][:-1] - representation.append( - ']' - ) - - representation[-1] += ')' - - return '\n'.join(representation) if len(representation) > 2 else ''.join(representation) - - -class Property(object): - """ - This is the base class for defining a property. - - Properties - - - value_types ([type|Property]): One or more expected value_types or `Property` instances. Values are checked, - sequentially, against each type or `Property` instance, and the first appropriate match is used. - - - required (bool|collections.Callable): If `True`--dumping the json_object will throw an error if this value - is `None`. - - - versions ([str]|{str:Property}): The property should be one of the following: - - - A set/tuple/list of version numbers to which this property applies. - - A mapping of version numbers to an instance of `Property` applicable to that version. - - Version numbers prefixed by "<" indicate any version less than the one specified, so "<3.0" indicates that - this property is available in versions prior to 3.0. The inverse is true for version numbers prefixed by ">". - ">=" and "<=" have similar meanings, but are inclusive. - - Versioning can be applied to an json_object by calling `serial.meta.set_version` in the `__init__` method of - an `serial.model.Object` sub-class. For an example, see `oapi.model.OpenAPI.__init__`. - - - name (str): The name of the property when loaded from or dumped into a JSON/YAML/XML json_object. Specifying a - `name` facilitates mapping of PEP8 compliant property to JSON or YAML attribute names, or XML element names, - which are either camelCased, are python keywords, or otherwise not appropriate for usage in python code. - - """ - - def __init__( - self, - types=None, # type: Sequence[Union[type, Property]] - name=None, # type: Optional[str] - required=False, # type: Union[bool, collections.Callable] - versions=None, # type: Optional[Sequence[Union[str, meta.Version]]] - ): - self._types = None # type: Optional[Sequence[Union[type, Property]]] - self.types = types - self.name = name - self.required = required - self._versions = None # type: Optional[Union[Mapping[str, Optional[Property]], Set[Union[str, Number]]]] - self.versions = versions # type: Optional[Union[Mapping[str, Optional[Property]], Set[Union[str, Number]]]] - - @property - def types(self): - return self._types - - @types.setter - def types(self, types_or_properties): - # type: (Optional[Sequence[Union[type, Property, abc.model.Model]]]) -> None - - if types_or_properties is not None: - - if callable(types_or_properties): - - if native_str is not str: - - _types_or_properties = types_or_properties - - def types_or_properties(d): - # type: (Sequence[Union[type, Property, abc.model.Model]]) -> Types - return Types(self, _types_or_properties(d)) - - else: - - types_or_properties = Types(self, types_or_properties) - - self._types = types_or_properties - - @property - def versions(self): - # type: () -> Optional[Sequence[meta.Version]] - return self._versions - - @versions.setter - def versions( - self, - versions # type: Optional[Sequence[Union[str, collections_abc.Iterable, meta.Version]]] - ): - # type: (...) -> Optional[Union[Mapping[str, Optional[Property]], Set[Union[str, Number]]]] - if versions is not None: - - if isinstance(versions, (str, Number, meta.Version)): - versions = (versions,) - - if isinstance(versions, collections_abc.Iterable): - versions = tuple( - (v if isinstance(v, meta.Version) else meta.Version(v)) - for v in versions - ) - - else: - - repr_versions = repr(versions) - - raise TypeError( - ( - '`%s` requires a sequence of version strings or ' % - calling_function_qualified_name() - ) + ( - '`%s` instances, not' % qualified_name(meta.Version) - ) + ( - ':\n' + repr_versions - if '\n' in repr_versions else - ' `%s`.' % repr_versions - ) - ) - - self._versions = versions - - def unmarshal(self, data): - # type: (Any) -> Any - # return data if self.types is None else unmarshal(data, types=self.types) - - if isinstance( - data, - collections_abc.Iterable - ) and not isinstance( - data, - (str, bytes, bytearray, native_str) - ) and not isinstance( - data, - abc.model.Model - ): - - if isinstance(data, (dict, collections.OrderedDict)): - - for k, v in data.items(): - if v is None: - data[k] = NULL - - else: - - data = tuple((NULL if i is None else i) for i in data) - - return serial.marshal.unmarshal(data, types=self.types) - - def marshal(self, data): - # type: (Any) -> Any - return serial.marshal.marshal(data, types=self.types) #, types=self.types, value_types=self.value_types) - - def __repr__(self): - representation = [qualified_name(type(self)) + '('] - pd = parameters_defaults(self.__init__) - for p, v in properties_values(self): - if (p not in pd) or pd[p] == v: - continue - if (v is not None) and (v is not NULL): - if isinstance(v, collections_abc.Sequence) and not isinstance(v, (str, bytes)): - rvs = ['('] - for i in v: - ri = ( - qualified_name(i) - if isinstance(i, type) else - "'%s'" % str(i) - if isinstance(i, meta.Version) else - repr(i) - ) - rils = ri.split('\n') - if len(rils) > 1: - ris = [rils[0]] - for ril in rils[1:]: - ris.append(' ' + ril) - ri = '\n'.join(ris) - rvs.append(' %s,' % ri) - if len(v) > 1: - rvs[-1] = rvs[-1][:-1] - rvs.append(' )') - rv = '\n'.join(rvs) - else: - rv = ( - qualified_name(v) - if isinstance(v, type) else - "'%s'" % str(v) - if isinstance(v, meta.Version) else - repr(v) - ) - rvls = rv.split('\n') - if len(rvls) > 2: - rvs = [rvls[0]] - for rvl in rvls[1:]: - rvs.append(' ' + rvl) - rv = '\n'.join(rvs) - representation.append( - ' %s=%s,' % (p, rv) - ) - representation.append(')') - if len(representation) > 2: - return '\n'.join(representation) - else: - return ''.join(representation) - - def __copy__(self): - - new_instance = self.__class__() - - for a in dir(self): - - if a[0] != '_' and a != 'data': - - v = getattr(self, a) - - if not callable(v): - setattr(new_instance, a, v) - - return new_instance - - def __deepcopy__(self, memo): - # type: (dict) -> Property - - new_instance = self.__class__() - - for a, v in properties_values(self): - setattr(new_instance, a, deepcopy(v, memo)) - - return new_instance - - -abc.properties.Property.register(Property) - - -class String(Property): - """ - See `serial.properties.Property` - """ - - def __init__( - self, - name=None, # type: Optional[str] - required=False, # type: Union[bool, collections.Callable] - versions=None, # type: Optional[Collection] - ): - super().__init__( - types=(str,), - name=name, - required=required, - versions=versions, - ) - - -class Date(Property): - """ - See `serial.properties.Property` - - Additional Properties: - - - marshal (collections.Callable): A function, taking one argument (a python `date` json_object), and returning - a date string in the desired format. The default is `date.isoformat`--returning an iso8601 compliant date - string. - - - unmarshal (collections.Callable): A function, taking one argument (a date string), and returning a python - `date` json_object. By default, this is `iso8601.parse_date`. - """ - - def __init__( - self, - name=None, # type: Optional[str] - required=False, # type: Union[bool, collections.Callable] - versions=None, # type: Optional[Collection] - date2str=date.isoformat, # type: Optional[Callable] - str2date=iso8601.parse_date # type: Callable - ): - super().__init__( - types=(date,), - name=name, - required=required, - versions=versions, - ) - self.date2str = date2str - self.str2date = str2date - - def unmarshal(self, data): - # type: (Optional[str]) -> Union[date, NoneType] - if data is None: - return data - else: - if isinstance(data, date): - date_ = data - elif isinstance(data, str): - date_ = self.str2date(data) - else: - raise TypeError( - '%s is not a `str`.' % repr(data) - ) - if isinstance(date_, date): - return date_ - else: - raise TypeError( - '"%s" is not a properly formatted date string.' % data - ) - - def marshal(self, data): - # type: (Optional[date]) -> Optional[str] - if data is None: - return data - else: - ds = self.date2str(data) - if not isinstance(ds, str): - if isinstance(ds, native_str): - ds = str(ds) - else: - raise TypeError( - 'The date2str function should return a `str`, not a `%s`: %s' % ( - type(ds).__name__, - repr(ds) - ) - ) - return ds - - -class DateTime(Property): - """ - See `serial.properties.Property` - - Additional Properties: - - - marshal (collections.Callable): A function, taking one argument (a python `datetime` json_object), and - returning a date-time string in the desired format. The default is `datetime.isoformat`--returning an - iso8601 compliant date-time string. - - - unmarshal (collections.Callable): A function, taking one argument (a datetime string), and returning a python - `datetime` json_object. By default, this is `iso8601.parse_date`. - """ - - def __init__( - self, - name=None, # type: Optional[str] - required=False, # type: Union[bool, collections.Callable] - versions=None, # type: Optional[Collection] - datetime2str=datetime.isoformat, # type: Optional[Callable] - str2datetime=iso8601.parse_date # type: Callable - ): - self.datetime2str = datetime2str - self.str2datetime = str2datetime - super().__init__( - types=(datetime,), - name=name, - required=required, - versions=versions, - ) - - def unmarshal(self, data): - # type: (Optional[str]) -> Union[datetime, NoneType] - if data is None: - return data - else: - if isinstance(data, datetime): - datetime_ = data - elif isinstance(data, str): - datetime_ = self.str2datetime(data) - else: - raise TypeError( - '%s is not a `str`.' % repr(data) - ) - if isinstance(datetime_, datetime): - return datetime_ - else: - raise TypeError( - '"%s" is not a properly formatted date-time string.' % data - ) - - def marshal(self, data): - # type: (Optional[datetime]) -> Optional[str] - if data is None: - return data - else: - datetime_string = self.datetime2str(data) - if not isinstance(datetime_string, str): - if isinstance(datetime_string, native_str): - datetime_string = str(datetime_string) - else: - repr_datetime_string = repr(datetime_string).strip() - raise TypeError( - 'The datetime2str function should return a `str`, not:' + ( - '\n' - if '\n' in repr_datetime_string else - ' ' - ) + repr_datetime_string - ) - return datetime_string - - -class Bytes(Property): - """ - See `serial.properties.Property` - """ - - def __init__( - self, - name=None, # type: Optional[str] - required=False, # type: bool - versions=None, # type: Optional[Collection] - ): - super().__init__( - types=(bytes, bytearray), - name=name, - required=required, - versions=versions, - ) - - def unmarshal(self, data): - # type: (str) -> Optional[bytes] - """ - Un-marshal a base-64 encoded string into bytes - """ - if data is None: - return data - elif isinstance(data, str): - return b64decode(data) - elif isinstance(data, bytes): - return data - else: - raise TypeError( - '`data` must be a base64 encoded `str` or `bytes`--not `%s`' % qualified_name(type(data)) - ) - - def marshal(self, data): - # type: (bytes) -> str - """ - Marshal bytes into a base-64 encoded string - """ - if (data is None) or isinstance(data, str): - return data - elif isinstance(data, bytes): - return str(b64encode(data), 'ascii') - else: - raise TypeError( - '`data` must be a base64 encoded `str` or `bytes`--not `%s`' % qualified_name(type(data)) - ) - - -class Enumerated(Property): - """ - See `serial.properties.Property`... - - + Properties: - - - values ([Any]): A list of possible values. If the parameter `types` is specified--typing is - applied prior to validation. - """ - - def __init__( - self, - types=None, # type: Optional[Sequence[Union[type, Property]]] - values=None, # type: Optional[Union[Sequence, Set]] - name=None, # type: Optional[str] - required=False, # type: Union[bool, collections.Callable] - versions=None, # type: Optional[Collection] - ): - self._values = None - - super().__init__( - types=types, - name=name, - required=required, - versions=versions - ) - - self.values = values # type: Optional[Sequence] - - @property - def values(self): - # type: () -> Optional[Union[Tuple, Callable]] - return self._values - - @values.setter - def values(self, values): - # type: (Iterable) -> None - - if not ((values is None) or callable(values)): - - if (values is not None) and (not isinstance(values, (collections_abc.Sequence, collections_abc.Set))): - raise TypeError( - '`values` must be a finite set or sequence, not `%s`.' % qualified_name(type(values)) - ) - - if values is not None: - values = [ - serial.marshal.unmarshal(v, types=self.types) - for v in values - ] - - self._values = values - - def unmarshal(self, data): - # type: (Any) -> Any - - if self.types is not None: - data = serial.marshal.unmarshal(data, types=self.types) - - if ( - (data is not None) and - (self.values is not None) and - (data not in self.values) - ): - raise ValueError( - 'The value provided is not a valid option:\n%s\n\n' % repr(data) + - 'Valid options include:\n%s' % ( - ', '.join(repr(t) for t in self.values) - ) - ) - return data - - -class Number(Property): - """ - See `serial.properties.Property` - """ - - def __init__( - self, - name=None, # type: Optional[str] - required=False, # type: Union[bool, collections.Callable] - versions=None, # type: Optional[Collection] - ): - # type: (...) -> None - super().__init__( - types=(numbers.Number,), - name=name, - required=required, - versions=versions, - ) - - -class Integer(Property): - """ - See `serial.properties.Property` - """ - - def __init__( - self, - name=None, # type: Optional[str] - required=False, # type: Union[bool, collections.Callable] - versions=None, # type: Optional[Collection] - ): - super().__init__( - types=(int,), - name=name, - required=required, - versions=versions, - ) - - # def unmarshal(self, data): - # # type: (Any) -> Any - # if data is None: - # return data - # else: - # return int(data) - # - # def marshal(self, data): - # # type: (Any) -> Any - # if data is None: - # return data - # else: - # return int(data) - - -class Boolean(Property): - """ - See `serial.properties.Property` - """ - - def __init__( - self, - name=None, # type: Optional[str] - required=False, # type: Union[bool, collections.Callable] - versions=None, # type: Optional[Collection] - ): - # type: (...) -> None - super().__init__( - types=(bool,), - name=name, - required=required, - versions=versions, - ) - - -class Array(Property): - """ - See `serial.properties.Property`... - - + Properties: - - - item_types (type|Property|[type|Property]): The type(s) of values/objects contained in the array. Similar to - `serial.properties.Property().value_types`, but applied to items in the array, not the array itself. - """ - - def __init__( - self, - item_types=None, # type: Optional[Union[type, Sequence[Union[type, Property]]]] - name=None, # type: Optional[str] - required=False, # type: Union[bool, collections.Callable] - versions=None, # type: Optional[Collection] - ): - self._item_types = None - self.item_types = item_types - super().__init__( - types=(serial.model.Array,), - name=name, - required=required, - versions=versions, - ) - - def unmarshal(self, data): - # type: (Any) -> Any - return serial.marshal.unmarshal(data, types=self.types, item_types=self.item_types) - - def marshal(self, data): - # type: (Any) -> Any - return serial.marshal.marshal(data, types=self.types, item_types=self.item_types) - - @property - def item_types(self): - return self._item_types - - @item_types.setter - def item_types(self, item_types): - # type: (Optional[Sequence[Union[type, Property, abc.model.Object]]]) -> None - if item_types is not None: - if callable(item_types): - if native_str is not str: - _item_types = item_types - - def item_types(d): - # type: (Sequence[Union[type, Property, abc.model.Object]]) -> Types - return Types(self, _item_types(d)) - else: - item_types = Types(self, item_types) - self._item_types = item_types - - -class Dictionary(Property): - """ - See `serial.properties.Property`... - - + Properties: - - - value_types (type|Property|[type|Property]): The type(s) of values/objects comprising the mapped - values. Similar to `serial.properties.Property.types`, but applies to *values* in the dictionary - object, not the dictionary itself. - """ - - def __init__( - self, - value_types=None, # type: Optional[Union[type, Sequence[Union[type, Property]]]] - name=None, # type: Optional[str] - required=False, # type: Union[bool, collections.Callable] - versions=None, # type: Optional[Collection] - ): - self._value_types = None - self.value_types = value_types - super().__init__( - types=(serial.model.Dictionary,), - name=name, - required=required, - versions=versions, - ) - - def unmarshal(self, data): - # type: (Any) -> Any - return serial.marshal.unmarshal(data, types=self.types, value_types=self.value_types) - - @property - def value_types(self): - return self._value_types - - @value_types.setter - def value_types(self, value_types_): - # type: (Optional[Sequence[Union[type, Property, abc.model.Object]]]) -> None - """ - The `types` can be either: - - - A sequence of types and/or `serial.properties.Property` instances. - - - A function which accepts exactly one argument (a dictionary), and which returns a sequence of types and/or - `serial.properties.Property` instances. - - If more than one type or property definition is provided, un-marshalling is attempted using each `value_type`, - in sequential order. If a value could be cast into more than one of the `types` without throwing a - `ValueError`, `TypeError`, or `serial.errors.ValidationError`, the value type occuring *first* in the sequence - will be used. - """ - - if value_types_ is not None: - - if callable(value_types_): - - if native_str is not str: - - original_value_types_ = value_types_ - - def value_types_(data): - # type: (Sequence[Union[type, Property, abc.model.Object]]) -> Types - return Types(self, original_value_types_(data)) - - else: - - value_types_ = Types(self, value_types_) - - self._value_types = value_types_ diff --git a/backend/venv/lib/python3.6/site-packages/serial/request.py b/backend/venv/lib/python3.6/site-packages/serial/request.py deleted file mode 100644 index 90ef336..0000000 --- a/backend/venv/lib/python3.6/site-packages/serial/request.py +++ /dev/null @@ -1,451 +0,0 @@ -""" -This module extends the functionality of `urllib.request.Request` to support multipart requests, to support passing -instances of serial models to the `data` parameter/property for `urllib.request.Request`, and to -support casting requests as `str` or `bytes` (typically for debugging purposes and/or to aid in producing -non-language-specific API documentation). -""" -# region Backwards Compatibility -from __future__ import nested_scopes, generators, division, absolute_import, with_statement, \ - print_function, unicode_literals -from .utilities.compatibility import backport - -backport() # noqa - -from future.utils import native_str - -import random -import re -import string -import urllib.request - -try: - from typing import Dict, Sequence, Set, Iterable -except ImportError: - Dict = Sequence = Set = None - -from serial.marshal import serialize -from .abc.model import Model -from .utilities import collections_abc - - -class Headers(object): - """ - A dictionary of headers for a `Request`, `Part`, or `MultipartRequest` instance. - """ - - def __init__(self, items, request): - # type: (Dict[str, str], Union[Part, Request]) -> None - self._dict = {} - self.request = request # type: Data - self.update(items) - - def pop(self, key, default=None): - # type: (str, Optional[str]) -> str - key = key.capitalize() - if hasattr(self.request, '_boundary'): - self.request._boundary = None - if hasattr(self.request, '_bytes'): - self.request._bytes = None - return self._dict.pop(key, default=default) - - def popitem(self): - # type: (str, Optional[str]) -> str - if hasattr(self.request, '_boundary'): - self.request._boundary = None - if hasattr(self.request, '_bytes'): - self.request._bytes = None - return self._dict.popitem() - - def setdefault(self, key, default=None): - # type: (str, Optional[str]) -> str - key = key.capitalize() - if hasattr(self.request, '_boundary'): - self.request._boundary = None - if hasattr(self.request, '_bytes'): - self.request._bytes = None - return self._dict.setdefault(key, default=default) - - def update(self, iterable=None, **kwargs): - # type: (Union[Dict[str, str], Sequence[Tuple[str, str]]], Union[Dict[str, str]]) -> None - cd = {} - if iterable is None: - d = kwargs - else: - d = dict(iterable, **kwargs) - for k, v in d.items(): - cd[k.capitalize()] = v - if hasattr(self.request, '_boundary'): - self.request._boundary = None - if hasattr(self.request, '_bytes'): - self.request._bytes = None - return self._dict.update(cd) - - def __delitem__(self, key): - # type: (str) -> None - key = key.capitalize() - if hasattr(self.request, '_boundary'): - self.request._boundary = None - if hasattr(self.request, '_bytes'): - self.request._bytes = None - del self._dict[key] - - def __setitem__(self, key, value): - # type: (str, str) -> None - key = key.capitalize() - if key != 'Content-length': - if hasattr(self.request, '_boundary'): - self.request._boundary = None - if hasattr(self.request, '_bytes'): - self.request._bytes = None - return self._dict.__setitem__(key, value) - - def __getitem__(self, key): - # type: (str) -> None - key = key.capitalize() - if key == 'Content-length': - data = self.request.data - if data is None: - content_length = 0 - else: - content_length = len(data) - value = str(content_length) - else: - try: - value = self._dict.__getitem__(key) - except KeyError as e: - if key == 'Content-type': - if hasattr(self.request, 'parts') and self.request.parts: - value = 'multipart/form-data' - if ( - (value is not None) and - value.strip().lower()[:9] == 'multipart' and - hasattr(self.request, 'boundary') - ): - value += '; boundary=' + str(self.request.boundary, encoding='utf-8') - return value - - def keys(self): - # type: (...) -> Iterable[str] - return (k for k in self) - - def values(self): - return (self[k] for k in self) - - def __len__(self): - return len(tuple(self)) - - def __iter__(self): - # type: (...) -> Iterable[str] - keys = set() - for k in self._dict.keys(): - keys.add(k) - yield k - if type(self.request) is not Part: - # *Always* include "Content-length" - if 'Content-length' not in keys: - yield 'Content-length' - if ( - hasattr(self.request, 'parts') and - self.request.parts and - ('Content-type' not in keys) - ): - yield 'Content-type' - - def __contains__(self, key): - # type: (str) -> bool - return True if key in self.keys() else False - - def items(self): - # type: (...) -> Iterable[Tuple[str, str]] - for k in self: - yield k, self[k] - - def copy(self): - # type: (...) -> Headers - return self.__class__( - self._dict, - request=self.request - ) - - def __copy__(self): - # type: (...) -> Headers - return self.copy() - - -class Data(object): - """ - One of a multipart request's parts. - """ - - def __init__( - self, - data=None, # type: Optional[Union[bytes, str, Sequence, Set, dict, Model]] - headers=None # type: Optional[Dict[str, str]] - ): - """ - Parameters: - - - data (bytes|str|collections.Sequence|collections.Set|dict|serial.abc.Model): The payload. - - - headers ({str: str}): A dictionary of headers (for this part of the request body, not the main request). - This should (almost) always include values for "Content-Disposition" and "Content-Type". - """ - self._bytes = None # type: Optional[bytes] - self._headers = None - self._data = None - self.headers = headers # type: Dict[str, str] - self.data = data # type: Optional[bytes] - - @property - def headers(self): - return self._headers - - @headers.setter - def headers(self, headers): - self._bytes = None - if headers is None: - headers = Headers({}, self) - elif isinstance(headers, Headers): - headers.request = self - else: - headers = Headers(headers, self) - self._headers = headers - - @property - def data(self): - return self._data - - @data.setter - def data(self, data): - # type: (Optional[Union[bytes, str, Sequence, Set, dict, Model]]) -> None - self._bytes = None - if data is not None: - serialize_type = None - if 'Content-type' in self.headers: - ct = self.headers['Content-type'] - if re.search(r'/json\b', ct) is not None: - serialize_type = 'json' - if re.search(r'/xml\b', ct) is not None: - serialize_type = 'xml' - if re.search(r'/yaml\b', ct) is not None: - serialize_type = 'yaml' - if isinstance(data, (Model, dict)) or ( - isinstance(data, (collections_abc.Sequence, collections_abc.Set)) and not - isinstance(data, (str, bytes)) - ): - data = serialize(data, serialize_type or 'json') - if isinstance(data, str): - data = bytes(data, encoding='utf-8') - self._data = data - - def __bytes__(self): - if self._bytes is None: - lines = [] - for k, v in self.headers.items(): - lines.append(bytes( - '%s: %s' % (k, v), - encoding='utf-8' - )) - lines.append(b'') - data = self.data - if data: - lines.append(self.data) - self._bytes = b'\r\n'.join(lines) + b'\r\n' - return self._bytes - - def __str__(self): - b = self.__bytes__() - if not isinstance(b, native_str): - b = repr(b)[2:-1].replace('\\r\\n', '\r\n').replace('\\n', '\n') - return b - - -class Part(Data): - - def __init__( - self, - data=None, # type: Optional[Union[bytes, str, Sequence, Set, dict, Model]] - headers=None, # type: Optional[Dict[str, str]] - parts=None # type: Optional[Sequence[Part]] - ): - """ - Parameters: - - - data (bytes|str|collections.Sequence|collections.Set|dict|serial.abc.Model): The payload. - - - headers ({str: str}): A dictionary of headers (for this part of the request body, not the main request). - This should (almost) always include values for "Content-Disposition" and "Content-Type". - """ - self._boundary = None # type: Optional[bytes] - self._parts = None # type: Optional[Parts] - self.parts = parts - Data.__init__(self, data=data, headers=headers) - - @property - def boundary(self): - """ - Calculates a boundary which is not contained in any of the request parts. - """ - if self._boundary is None: - data = b'\r\n'.join( - [self._data or b''] + - [bytes(p) for p in self.parts] - ) - boundary = b''.join( - bytes( - random.choice(string.digits + string.ascii_letters), - encoding='utf-8' - ) - for i in range(16) - ) - while boundary in data: - boundary += bytes( - random.choice(string.digits + string.ascii_letters), - encoding='utf-8' - ) - self._boundary = boundary - return self._boundary - - @property - def data(self): - # type: (bytes) -> None - if self.parts: - data = (b'\r\n--' + self.boundary + b'\r\n').join( - [self._data or b''] + - [bytes(p).rstrip() for p in self.parts] - ) + (b'\r\n--' + self.boundary + b'--') - else: - data = self._data - return data - - @data.setter - def data(self, data): - return Data.data.__set__(self, data) - - @property - def parts(self): - # type: (...) -> Parts - return self._parts - - @parts.setter - def parts(self, parts): - # type: (Optional[Sequence[Part]]) -> None - if parts is None: - parts = Parts([], request=self) - elif isinstance(parts, Parts): - parts.request = self - else: - parts = Parts(parts, request=self) - self._boundary = None - self._parts = parts - - -class Parts(list): - - def __init__(self, items, request): - # type: (typing.Sequence[Part], MultipartRequest) -> None - self.request = request - super().__init__(items) - - def append(self, item): - # type: (Part) -> None - self.request._boundary = None - self.request._bytes = None - super().append(item) - - def clear(self): - # type: (...) -> None - self.request._boundary = None - self.request._bytes = None - super().clear() - - def extend(self, items): - # type: (Iterable[Part]) -> None - self.request._boundary = None - self.request._bytes = None - super().extend(items) - - def reverse(self): - # type: (...) -> None - self.request._boundary = None - self.request._bytes = None - super().reverse() - - def __delitem__(self, key): - # type: (str) -> None - self.request._boundary = None - self.request._bytes = None - super().__delitem__(key) - - def __setitem__(self, key, value): - # type: (str) -> None - self.request._boundary = None - self.request._bytes = None - super().__setitem__(key, value) - - -class Request(Data, urllib.request.Request): - """ - A sub-class of `urllib.request.Request` which accommodates additional data types, and serializes `data` in - accordance with what is indicated by the request's "Content-Type" header. - """ - - def __init__( - self, - url, - data=None, # type: Optional[Union[bytes, str, Sequence, Set, dict, Model]] - headers=None, # type: Optional[Dict[str, str]] - origin_req_host=None, # type: Optional[str] - unverifiable=False, # type: bool - method=None # type: Optional[str] - ): - # type: (...) -> None - self._bytes = None # type: Optional[bytes] - self._headers = None - self._data = None - self.headers = headers - urllib.request.Request.__init__( - self, - url, - data=data, - headers=headers, - origin_req_host=origin_req_host, - unverifiable=unverifiable, - method=method - ) - - -class MultipartRequest(Part, Request): - """ - A sub-class of `Request` which adds a property (and initialization parameter) to hold the `parts` of a - multipart request. - - https://www.w3.org/Protocols/rfc1341/7_2_Multipart.html - """ - - def __init__( - self, - url, - data=None, # type: Optional[Union[bytes, str, Sequence, Set, dict, Model]] - headers=None, # type: Optional[Dict[str, str]] - origin_req_host=None, # type: Optional[str] - unverifiable=False, # type: bool - method=None, # type: Optional[str] - parts=None # type: Optional[Sequence[Part]] - ): - # type: (...) -> None - Part.__init__( - self, - data=data, - headers=headers, - parts=parts - ) - Request.__init__( - self, - url, - data=data, - headers=headers, - origin_req_host=origin_req_host, - unverifiable=unverifiable, - method=method - ) diff --git a/backend/venv/lib/python3.6/site-packages/serial/rfc2217.py b/backend/venv/lib/python3.6/site-packages/serial/rfc2217.py new file mode 100644 index 0000000..419947d --- /dev/null +++ b/backend/venv/lib/python3.6/site-packages/serial/rfc2217.py @@ -0,0 +1,1346 @@ +#! python +# +# This module implements a RFC2217 compatible client. RF2217 descibes a +# protocol to access serial ports over TCP/IP and allows setting the baud rate, +# modem control lines etc. +# +# This file is part of pySerial. https://github.com/pyserial/pyserial +# (C) 2001-2015 Chris Liechti +# +# SPDX-License-Identifier: BSD-3-Clause + +# TODO: +# - setting control line -> answer is not checked (had problems with one of the +# severs). consider implementing a compatibility mode flag to make check +# conditional +# - write timeout not implemented at all + +# ########################################################################### +# observations and issues with servers +# =========================================================================== +# sredird V2.2.1 +# - http://www.ibiblio.org/pub/Linux/system/serial/ sredird-2.2.2.tar.gz +# - does not acknowledge SET_CONTROL (RTS/DTR) correctly, always responding +# [105 1] instead of the actual value. +# - SET_BAUDRATE answer contains 4 extra null bytes -> probably for larger +# numbers than 2**32? +# - To get the signature [COM_PORT_OPTION 0] has to be sent. +# - run a server: while true; do nc -l -p 7000 -c "sredird debug /dev/ttyUSB0 /var/lock/sredir"; done +# =========================================================================== +# telnetcpcd (untested) +# - http://ftp.wayne.edu/kermit/sredird/telnetcpcd-1.09.tar.gz +# - To get the signature [COM_PORT_OPTION] w/o data has to be sent. +# =========================================================================== +# ser2net +# - does not negotiate BINARY or COM_PORT_OPTION for his side but at least +# acknowledges that the client activates these options +# - The configuration may be that the server prints a banner. As this client +# implementation does a flushInput on connect, this banner is hidden from +# the user application. +# - NOTIFY_MODEMSTATE: the poll interval of the server seems to be one +# second. +# - To get the signature [COM_PORT_OPTION 0] has to be sent. +# - run a server: run ser2net daemon, in /etc/ser2net.conf: +# 2000:telnet:0:/dev/ttyS0:9600 remctl banner +# ########################################################################### + +# How to identify ports? pySerial might want to support other protocols in the +# future, so lets use an URL scheme. +# for RFC2217 compliant servers we will use this: +# rfc2217://:[?option[&option...]] +# +# options: +# - "logging" set log level print diagnostic messages (e.g. "logging=debug") +# - "ign_set_control": do not look at the answers to SET_CONTROL +# - "poll_modem": issue NOTIFY_MODEMSTATE requests when CTS/DTR/RI/CD is read. +# Without this option it expects that the server sends notifications +# automatically on change (which most servers do and is according to the +# RFC). +# the order of the options is not relevant + +import logging +import socket +import struct +import threading +import time +try: + import urlparse +except ImportError: + import urllib.parse as urlparse +try: + import Queue +except ImportError: + import queue as Queue + +import serial +from serial.serialutil import SerialBase, SerialException, to_bytes, \ + iterbytes, portNotOpenError, Timeout + +# port string is expected to be something like this: +# rfc2217://host:port +# host may be an IP or including domain, whatever. +# port is 0...65535 + +# map log level names to constants. used in from_url() +LOGGER_LEVELS = { + 'debug': logging.DEBUG, + 'info': logging.INFO, + 'warning': logging.WARNING, + 'error': logging.ERROR, +} + + +# telnet protocol characters +SE = b'\xf0' # Subnegotiation End +NOP = b'\xf1' # No Operation +DM = b'\xf2' # Data Mark +BRK = b'\xf3' # Break +IP = b'\xf4' # Interrupt process +AO = b'\xf5' # Abort output +AYT = b'\xf6' # Are You There +EC = b'\xf7' # Erase Character +EL = b'\xf8' # Erase Line +GA = b'\xf9' # Go Ahead +SB = b'\xfa' # Subnegotiation Begin +WILL = b'\xfb' +WONT = b'\xfc' +DO = b'\xfd' +DONT = b'\xfe' +IAC = b'\xff' # Interpret As Command +IAC_DOUBLED = b'\xff\xff' + +# selected telnet options +BINARY = b'\x00' # 8-bit data path +ECHO = b'\x01' # echo +SGA = b'\x03' # suppress go ahead + +# RFC2217 +COM_PORT_OPTION = b'\x2c' + +# Client to Access Server +SET_BAUDRATE = b'\x01' +SET_DATASIZE = b'\x02' +SET_PARITY = b'\x03' +SET_STOPSIZE = b'\x04' +SET_CONTROL = b'\x05' +NOTIFY_LINESTATE = b'\x06' +NOTIFY_MODEMSTATE = b'\x07' +FLOWCONTROL_SUSPEND = b'\x08' +FLOWCONTROL_RESUME = b'\x09' +SET_LINESTATE_MASK = b'\x0a' +SET_MODEMSTATE_MASK = b'\x0b' +PURGE_DATA = b'\x0c' + +SERVER_SET_BAUDRATE = b'\x65' +SERVER_SET_DATASIZE = b'\x66' +SERVER_SET_PARITY = b'\x67' +SERVER_SET_STOPSIZE = b'\x68' +SERVER_SET_CONTROL = b'\x69' +SERVER_NOTIFY_LINESTATE = b'\x6a' +SERVER_NOTIFY_MODEMSTATE = b'\x6b' +SERVER_FLOWCONTROL_SUSPEND = b'\x6c' +SERVER_FLOWCONTROL_RESUME = b'\x6d' +SERVER_SET_LINESTATE_MASK = b'\x6e' +SERVER_SET_MODEMSTATE_MASK = b'\x6f' +SERVER_PURGE_DATA = b'\x70' + +RFC2217_ANSWER_MAP = { + SET_BAUDRATE: SERVER_SET_BAUDRATE, + SET_DATASIZE: SERVER_SET_DATASIZE, + SET_PARITY: SERVER_SET_PARITY, + SET_STOPSIZE: SERVER_SET_STOPSIZE, + SET_CONTROL: SERVER_SET_CONTROL, + NOTIFY_LINESTATE: SERVER_NOTIFY_LINESTATE, + NOTIFY_MODEMSTATE: SERVER_NOTIFY_MODEMSTATE, + FLOWCONTROL_SUSPEND: SERVER_FLOWCONTROL_SUSPEND, + FLOWCONTROL_RESUME: SERVER_FLOWCONTROL_RESUME, + SET_LINESTATE_MASK: SERVER_SET_LINESTATE_MASK, + SET_MODEMSTATE_MASK: SERVER_SET_MODEMSTATE_MASK, + PURGE_DATA: SERVER_PURGE_DATA, +} + +SET_CONTROL_REQ_FLOW_SETTING = b'\x00' # Request Com Port Flow Control Setting (outbound/both) +SET_CONTROL_USE_NO_FLOW_CONTROL = b'\x01' # Use No Flow Control (outbound/both) +SET_CONTROL_USE_SW_FLOW_CONTROL = b'\x02' # Use XON/XOFF Flow Control (outbound/both) +SET_CONTROL_USE_HW_FLOW_CONTROL = b'\x03' # Use HARDWARE Flow Control (outbound/both) +SET_CONTROL_REQ_BREAK_STATE = b'\x04' # Request BREAK State +SET_CONTROL_BREAK_ON = b'\x05' # Set BREAK State ON +SET_CONTROL_BREAK_OFF = b'\x06' # Set BREAK State OFF +SET_CONTROL_REQ_DTR = b'\x07' # Request DTR Signal State +SET_CONTROL_DTR_ON = b'\x08' # Set DTR Signal State ON +SET_CONTROL_DTR_OFF = b'\x09' # Set DTR Signal State OFF +SET_CONTROL_REQ_RTS = b'\x0a' # Request RTS Signal State +SET_CONTROL_RTS_ON = b'\x0b' # Set RTS Signal State ON +SET_CONTROL_RTS_OFF = b'\x0c' # Set RTS Signal State OFF +SET_CONTROL_REQ_FLOW_SETTING_IN = b'\x0d' # Request Com Port Flow Control Setting (inbound) +SET_CONTROL_USE_NO_FLOW_CONTROL_IN = b'\x0e' # Use No Flow Control (inbound) +SET_CONTROL_USE_SW_FLOW_CONTOL_IN = b'\x0f' # Use XON/XOFF Flow Control (inbound) +SET_CONTROL_USE_HW_FLOW_CONTOL_IN = b'\x10' # Use HARDWARE Flow Control (inbound) +SET_CONTROL_USE_DCD_FLOW_CONTROL = b'\x11' # Use DCD Flow Control (outbound/both) +SET_CONTROL_USE_DTR_FLOW_CONTROL = b'\x12' # Use DTR Flow Control (inbound) +SET_CONTROL_USE_DSR_FLOW_CONTROL = b'\x13' # Use DSR Flow Control (outbound/both) + +LINESTATE_MASK_TIMEOUT = 128 # Time-out Error +LINESTATE_MASK_SHIFTREG_EMPTY = 64 # Transfer Shift Register Empty +LINESTATE_MASK_TRANSREG_EMPTY = 32 # Transfer Holding Register Empty +LINESTATE_MASK_BREAK_DETECT = 16 # Break-detect Error +LINESTATE_MASK_FRAMING_ERROR = 8 # Framing Error +LINESTATE_MASK_PARTIY_ERROR = 4 # Parity Error +LINESTATE_MASK_OVERRUN_ERROR = 2 # Overrun Error +LINESTATE_MASK_DATA_READY = 1 # Data Ready + +MODEMSTATE_MASK_CD = 128 # Receive Line Signal Detect (also known as Carrier Detect) +MODEMSTATE_MASK_RI = 64 # Ring Indicator +MODEMSTATE_MASK_DSR = 32 # Data-Set-Ready Signal State +MODEMSTATE_MASK_CTS = 16 # Clear-To-Send Signal State +MODEMSTATE_MASK_CD_CHANGE = 8 # Delta Receive Line Signal Detect +MODEMSTATE_MASK_RI_CHANGE = 4 # Trailing-edge Ring Detector +MODEMSTATE_MASK_DSR_CHANGE = 2 # Delta Data-Set-Ready +MODEMSTATE_MASK_CTS_CHANGE = 1 # Delta Clear-To-Send + +PURGE_RECEIVE_BUFFER = b'\x01' # Purge access server receive data buffer +PURGE_TRANSMIT_BUFFER = b'\x02' # Purge access server transmit data buffer +PURGE_BOTH_BUFFERS = b'\x03' # Purge both the access server receive data + # buffer and the access server transmit data buffer + + +RFC2217_PARITY_MAP = { + serial.PARITY_NONE: 1, + serial.PARITY_ODD: 2, + serial.PARITY_EVEN: 3, + serial.PARITY_MARK: 4, + serial.PARITY_SPACE: 5, +} +RFC2217_REVERSE_PARITY_MAP = dict((v, k) for k, v in RFC2217_PARITY_MAP.items()) + +RFC2217_STOPBIT_MAP = { + serial.STOPBITS_ONE: 1, + serial.STOPBITS_ONE_POINT_FIVE: 3, + serial.STOPBITS_TWO: 2, +} +RFC2217_REVERSE_STOPBIT_MAP = dict((v, k) for k, v in RFC2217_STOPBIT_MAP.items()) + +# Telnet filter states +M_NORMAL = 0 +M_IAC_SEEN = 1 +M_NEGOTIATE = 2 + +# TelnetOption and TelnetSubnegotiation states +REQUESTED = 'REQUESTED' +ACTIVE = 'ACTIVE' +INACTIVE = 'INACTIVE' +REALLY_INACTIVE = 'REALLY_INACTIVE' + + +class TelnetOption(object): + """Manage a single telnet option, keeps track of DO/DONT WILL/WONT.""" + + def __init__(self, connection, name, option, send_yes, send_no, ack_yes, + ack_no, initial_state, activation_callback=None): + """\ + Initialize option. + :param connection: connection used to transmit answers + :param name: a readable name for debug outputs + :param send_yes: what to send when option is to be enabled. + :param send_no: what to send when option is to be disabled. + :param ack_yes: what to expect when remote agrees on option. + :param ack_no: what to expect when remote disagrees on option. + :param initial_state: options initialized with REQUESTED are tried to + be enabled on startup. use INACTIVE for all others. + """ + self.connection = connection + self.name = name + self.option = option + self.send_yes = send_yes + self.send_no = send_no + self.ack_yes = ack_yes + self.ack_no = ack_no + self.state = initial_state + self.active = False + self.activation_callback = activation_callback + + def __repr__(self): + """String for debug outputs""" + return "{o.name}:{o.active}({o.state})".format(o=self) + + def process_incoming(self, command): + """\ + A DO/DONT/WILL/WONT was received for this option, update state and + answer when needed. + """ + if command == self.ack_yes: + if self.state is REQUESTED: + self.state = ACTIVE + self.active = True + if self.activation_callback is not None: + self.activation_callback() + elif self.state is ACTIVE: + pass + elif self.state is INACTIVE: + self.state = ACTIVE + self.connection.telnet_send_option(self.send_yes, self.option) + self.active = True + if self.activation_callback is not None: + self.activation_callback() + elif self.state is REALLY_INACTIVE: + self.connection.telnet_send_option(self.send_no, self.option) + else: + raise ValueError('option in illegal state {!r}'.format(self)) + elif command == self.ack_no: + if self.state is REQUESTED: + self.state = INACTIVE + self.active = False + elif self.state is ACTIVE: + self.state = INACTIVE + self.connection.telnet_send_option(self.send_no, self.option) + self.active = False + elif self.state is INACTIVE: + pass + elif self.state is REALLY_INACTIVE: + pass + else: + raise ValueError('option in illegal state {!r}'.format(self)) + + +class TelnetSubnegotiation(object): + """\ + A object to handle subnegotiation of options. In this case actually + sub-sub options for RFC 2217. It is used to track com port options. + """ + + def __init__(self, connection, name, option, ack_option=None): + if ack_option is None: + ack_option = option + self.connection = connection + self.name = name + self.option = option + self.value = None + self.ack_option = ack_option + self.state = INACTIVE + + def __repr__(self): + """String for debug outputs.""" + return "{sn.name}:{sn.state}".format(sn=self) + + def set(self, value): + """\ + Request a change of the value. a request is sent to the server. if + the client needs to know if the change is performed he has to check the + state of this object. + """ + self.value = value + self.state = REQUESTED + self.connection.rfc2217_send_subnegotiation(self.option, self.value) + if self.connection.logger: + self.connection.logger.debug("SB Requesting {} -> {!r}".format(self.name, self.value)) + + def is_ready(self): + """\ + Check if answer from server has been received. when server rejects + the change, raise a ValueError. + """ + if self.state == REALLY_INACTIVE: + raise ValueError("remote rejected value for option {!r}".format(self.name)) + return self.state == ACTIVE + # add property to have a similar interface as TelnetOption + active = property(is_ready) + + def wait(self, timeout=3): + """\ + Wait until the subnegotiation has been acknowledged or timeout. It + can also throw a value error when the answer from the server does not + match the value sent. + """ + timeout_timer = Timeout(timeout) + while not timeout_timer.expired(): + time.sleep(0.05) # prevent 100% CPU load + if self.is_ready(): + break + else: + raise SerialException("timeout while waiting for option {!r}".format(self.name)) + + def check_answer(self, suboption): + """\ + Check an incoming subnegotiation block. The parameter already has + cut off the header like sub option number and com port option value. + """ + if self.value == suboption[:len(self.value)]: + self.state = ACTIVE + else: + # error propagation done in is_ready + self.state = REALLY_INACTIVE + if self.connection.logger: + self.connection.logger.debug("SB Answer {} -> {!r} -> {}".format(self.name, suboption, self.state)) + + +class Serial(SerialBase): + """Serial port implementation for RFC 2217 remote serial ports.""" + + BAUDRATES = (50, 75, 110, 134, 150, 200, 300, 600, 1200, 1800, 2400, 4800, + 9600, 19200, 38400, 57600, 115200) + + def __init__(self, *args, **kwargs): + self._thread = None + self._socket = None + self._linestate = 0 + self._modemstate = None + self._modemstate_timeout = Timeout(-1) + self._remote_suspend_flow = False + self._write_lock = None + self.logger = None + self._ignore_set_control_answer = False + self._poll_modem_state = False + self._network_timeout = 3 + self._telnet_options = None + self._rfc2217_port_settings = None + self._rfc2217_options = None + self._read_buffer = None + super(Serial, self).__init__(*args, **kwargs) # must be last call in case of auto-open + + def open(self): + """\ + Open port with current settings. This may throw a SerialException + if the port cannot be opened. + """ + self.logger = None + self._ignore_set_control_answer = False + self._poll_modem_state = False + self._network_timeout = 3 + if self._port is None: + raise SerialException("Port must be configured before it can be used.") + if self.is_open: + raise SerialException("Port is already open.") + try: + self._socket = socket.create_connection(self.from_url(self.portstr), timeout=5) # XXX good value? + self._socket.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1) + except Exception as msg: + self._socket = None + raise SerialException("Could not open port {}: {}".format(self.portstr, msg)) + + # use a thread save queue as buffer. it also simplifies implementing + # the read timeout + self._read_buffer = Queue.Queue() + # to ensure that user writes does not interfere with internal + # telnet/rfc2217 options establish a lock + self._write_lock = threading.Lock() + # name the following separately so that, below, a check can be easily done + mandadory_options = [ + TelnetOption(self, 'we-BINARY', BINARY, WILL, WONT, DO, DONT, INACTIVE), + TelnetOption(self, 'we-RFC2217', COM_PORT_OPTION, WILL, WONT, DO, DONT, REQUESTED), + ] + # all supported telnet options + self._telnet_options = [ + TelnetOption(self, 'ECHO', ECHO, DO, DONT, WILL, WONT, REQUESTED), + TelnetOption(self, 'we-SGA', SGA, WILL, WONT, DO, DONT, REQUESTED), + TelnetOption(self, 'they-SGA', SGA, DO, DONT, WILL, WONT, REQUESTED), + TelnetOption(self, 'they-BINARY', BINARY, DO, DONT, WILL, WONT, INACTIVE), + TelnetOption(self, 'they-RFC2217', COM_PORT_OPTION, DO, DONT, WILL, WONT, REQUESTED), + ] + mandadory_options + # RFC 2217 specific states + # COM port settings + self._rfc2217_port_settings = { + 'baudrate': TelnetSubnegotiation(self, 'baudrate', SET_BAUDRATE, SERVER_SET_BAUDRATE), + 'datasize': TelnetSubnegotiation(self, 'datasize', SET_DATASIZE, SERVER_SET_DATASIZE), + 'parity': TelnetSubnegotiation(self, 'parity', SET_PARITY, SERVER_SET_PARITY), + 'stopsize': TelnetSubnegotiation(self, 'stopsize', SET_STOPSIZE, SERVER_SET_STOPSIZE), + } + # There are more subnegotiation objects, combine all in one dictionary + # for easy access + self._rfc2217_options = { + 'purge': TelnetSubnegotiation(self, 'purge', PURGE_DATA, SERVER_PURGE_DATA), + 'control': TelnetSubnegotiation(self, 'control', SET_CONTROL, SERVER_SET_CONTROL), + } + self._rfc2217_options.update(self._rfc2217_port_settings) + # cache for line and modem states that the server sends to us + self._linestate = 0 + self._modemstate = None + self._modemstate_timeout = Timeout(-1) + # RFC 2217 flow control between server and client + self._remote_suspend_flow = False + + self.is_open = True + self._thread = threading.Thread(target=self._telnet_read_loop) + self._thread.setDaemon(True) + self._thread.setName('pySerial RFC 2217 reader thread for {}'.format(self._port)) + self._thread.start() + + try: # must clean-up if open fails + # negotiate Telnet/RFC 2217 -> send initial requests + for option in self._telnet_options: + if option.state is REQUESTED: + self.telnet_send_option(option.send_yes, option.option) + # now wait until important options are negotiated + timeout = Timeout(self._network_timeout) + while not timeout.expired(): + time.sleep(0.05) # prevent 100% CPU load + if sum(o.active for o in mandadory_options) == sum(o.state != INACTIVE for o in mandadory_options): + break + else: + raise SerialException( + "Remote does not seem to support RFC2217 or BINARY mode {!r}".format(mandadory_options)) + if self.logger: + self.logger.info("Negotiated options: {}".format(self._telnet_options)) + + # fine, go on, set RFC 2271 specific things + self._reconfigure_port() + # all things set up get, now a clean start + if not self._dsrdtr: + self._update_dtr_state() + if not self._rtscts: + self._update_rts_state() + self.reset_input_buffer() + self.reset_output_buffer() + except: + self.close() + raise + + def _reconfigure_port(self): + """Set communication parameters on opened port.""" + if self._socket is None: + raise SerialException("Can only operate on open ports") + + # if self._timeout != 0 and self._interCharTimeout is not None: + # XXX + + if self._write_timeout is not None: + raise NotImplementedError('write_timeout is currently not supported') + # XXX + + # Setup the connection + # to get good performance, all parameter changes are sent first... + if not 0 < self._baudrate < 2 ** 32: + raise ValueError("invalid baudrate: {!r}".format(self._baudrate)) + self._rfc2217_port_settings['baudrate'].set(struct.pack(b'!I', self._baudrate)) + self._rfc2217_port_settings['datasize'].set(struct.pack(b'!B', self._bytesize)) + self._rfc2217_port_settings['parity'].set(struct.pack(b'!B', RFC2217_PARITY_MAP[self._parity])) + self._rfc2217_port_settings['stopsize'].set(struct.pack(b'!B', RFC2217_STOPBIT_MAP[self._stopbits])) + + # and now wait until parameters are active + items = self._rfc2217_port_settings.values() + if self.logger: + self.logger.debug("Negotiating settings: {}".format(items)) + timeout = Timeout(self._network_timeout) + while not timeout.expired(): + time.sleep(0.05) # prevent 100% CPU load + if sum(o.active for o in items) == len(items): + break + else: + raise SerialException("Remote does not accept parameter change (RFC2217): {!r}".format(items)) + if self.logger: + self.logger.info("Negotiated settings: {}".format(items)) + + if self._rtscts and self._xonxoff: + raise ValueError('xonxoff and rtscts together are not supported') + elif self._rtscts: + self.rfc2217_set_control(SET_CONTROL_USE_HW_FLOW_CONTROL) + elif self._xonxoff: + self.rfc2217_set_control(SET_CONTROL_USE_SW_FLOW_CONTROL) + else: + self.rfc2217_set_control(SET_CONTROL_USE_NO_FLOW_CONTROL) + + def close(self): + """Close port""" + self.is_open = False + if self._socket: + try: + self._socket.shutdown(socket.SHUT_RDWR) + self._socket.close() + except: + # ignore errors. + pass + if self._thread: + self._thread.join(7) # XXX more than socket timeout + self._thread = None + # in case of quick reconnects, give the server some time + time.sleep(0.3) + self._socket = None + + def from_url(self, url): + """\ + extract host and port from an URL string, other settings are extracted + an stored in instance + """ + parts = urlparse.urlsplit(url) + if parts.scheme != "rfc2217": + raise SerialException( + 'expected a string in the form ' + '"rfc2217://:[?option[&option...]]": ' + 'not starting with rfc2217:// ({!r})'.format(parts.scheme)) + try: + # process options now, directly altering self + for option, values in urlparse.parse_qs(parts.query, True).items(): + if option == 'logging': + logging.basicConfig() # XXX is that good to call it here? + self.logger = logging.getLogger('pySerial.rfc2217') + self.logger.setLevel(LOGGER_LEVELS[values[0]]) + self.logger.debug('enabled logging') + elif option == 'ign_set_control': + self._ignore_set_control_answer = True + elif option == 'poll_modem': + self._poll_modem_state = True + elif option == 'timeout': + self._network_timeout = float(values[0]) + else: + raise ValueError('unknown option: {!r}'.format(option)) + if not 0 <= parts.port < 65536: + raise ValueError("port not in range 0...65535") + except ValueError as e: + raise SerialException( + 'expected a string in the form ' + '"rfc2217://:[?option[&option...]]": {}'.format(e)) + return (parts.hostname, parts.port) + + # - - - - - - - - - - - - - - - - - - - - - - - - + + @property + def in_waiting(self): + """Return the number of bytes currently in the input buffer.""" + if not self.is_open: + raise portNotOpenError + return self._read_buffer.qsize() + + def read(self, size=1): + """\ + Read size bytes from the serial port. If a timeout is set it may + return less characters as requested. With no timeout it will block + until the requested number of bytes is read. + """ + if not self.is_open: + raise portNotOpenError + data = bytearray() + try: + timeout = Timeout(self._timeout) + while len(data) < size: + if self._thread is None: + raise SerialException('connection failed (reader thread died)') + data += self._read_buffer.get(True, timeout.time_left()) + if timeout.expired(): + break + except Queue.Empty: # -> timeout + pass + return bytes(data) + + def write(self, data): + """\ + Output the given byte string over the serial port. Can block if the + connection is blocked. May raise SerialException if the connection is + closed. + """ + if not self.is_open: + raise portNotOpenError + # XXX use protocol_socket's write + with self._write_lock: + try: + self._socket.sendall(to_bytes(data).replace(IAC, IAC_DOUBLED)) + except socket.error as e: + raise SerialException("connection failed (socket error): {}".format(e)) + return len(data) + + def reset_input_buffer(self): + """Clear input buffer, discarding all that is in the buffer.""" + if not self.is_open: + raise portNotOpenError + self.rfc2217_send_purge(PURGE_RECEIVE_BUFFER) + # empty read buffer + while self._read_buffer.qsize(): + self._read_buffer.get(False) + + def reset_output_buffer(self): + """\ + Clear output buffer, aborting the current output and + discarding all that is in the buffer. + """ + if not self.is_open: + raise portNotOpenError + self.rfc2217_send_purge(PURGE_TRANSMIT_BUFFER) + + def _update_break_state(self): + """\ + Set break: Controls TXD. When active, to transmitting is + possible. + """ + if not self.is_open: + raise portNotOpenError + if self.logger: + self.logger.info('set BREAK to {}'.format('active' if self._break_state else 'inactive')) + if self._break_state: + self.rfc2217_set_control(SET_CONTROL_BREAK_ON) + else: + self.rfc2217_set_control(SET_CONTROL_BREAK_OFF) + + def _update_rts_state(self): + """Set terminal status line: Request To Send.""" + if not self.is_open: + raise portNotOpenError + if self.logger: + self.logger.info('set RTS to {}'.format('active' if self._rts_state else 'inactive')) + if self._rts_state: + self.rfc2217_set_control(SET_CONTROL_RTS_ON) + else: + self.rfc2217_set_control(SET_CONTROL_RTS_OFF) + + def _update_dtr_state(self): + """Set terminal status line: Data Terminal Ready.""" + if not self.is_open: + raise portNotOpenError + if self.logger: + self.logger.info('set DTR to {}'.format('active' if self._dtr_state else 'inactive')) + if self._dtr_state: + self.rfc2217_set_control(SET_CONTROL_DTR_ON) + else: + self.rfc2217_set_control(SET_CONTROL_DTR_OFF) + + @property + def cts(self): + """Read terminal status line: Clear To Send.""" + if not self.is_open: + raise portNotOpenError + return bool(self.get_modem_state() & MODEMSTATE_MASK_CTS) + + @property + def dsr(self): + """Read terminal status line: Data Set Ready.""" + if not self.is_open: + raise portNotOpenError + return bool(self.get_modem_state() & MODEMSTATE_MASK_DSR) + + @property + def ri(self): + """Read terminal status line: Ring Indicator.""" + if not self.is_open: + raise portNotOpenError + return bool(self.get_modem_state() & MODEMSTATE_MASK_RI) + + @property + def cd(self): + """Read terminal status line: Carrier Detect.""" + if not self.is_open: + raise portNotOpenError + return bool(self.get_modem_state() & MODEMSTATE_MASK_CD) + + # - - - platform specific - - - + # None so far + + # - - - RFC2217 specific - - - + + def _telnet_read_loop(self): + """Read loop for the socket.""" + mode = M_NORMAL + suboption = None + try: + while self.is_open: + try: + data = self._socket.recv(1024) + except socket.timeout: + # just need to get out of recv form time to time to check if + # still alive + continue + except socket.error as e: + # connection fails -> terminate loop + if self.logger: + self.logger.debug("socket error in reader thread: {}".format(e)) + break + if not data: + break # lost connection + for byte in iterbytes(data): + if mode == M_NORMAL: + # interpret as command or as data + if byte == IAC: + mode = M_IAC_SEEN + else: + # store data in read buffer or sub option buffer + # depending on state + if suboption is not None: + suboption += byte + else: + self._read_buffer.put(byte) + elif mode == M_IAC_SEEN: + if byte == IAC: + # interpret as command doubled -> insert character + # itself + if suboption is not None: + suboption += IAC + else: + self._read_buffer.put(IAC) + mode = M_NORMAL + elif byte == SB: + # sub option start + suboption = bytearray() + mode = M_NORMAL + elif byte == SE: + # sub option end -> process it now + self._telnet_process_subnegotiation(bytes(suboption)) + suboption = None + mode = M_NORMAL + elif byte in (DO, DONT, WILL, WONT): + # negotiation + telnet_command = byte + mode = M_NEGOTIATE + else: + # other telnet commands + self._telnet_process_command(byte) + mode = M_NORMAL + elif mode == M_NEGOTIATE: # DO, DONT, WILL, WONT was received, option now following + self._telnet_negotiate_option(telnet_command, byte) + mode = M_NORMAL + finally: + self._thread = None + if self.logger: + self.logger.debug("read thread terminated") + + # - incoming telnet commands and options + + def _telnet_process_command(self, command): + """Process commands other than DO, DONT, WILL, WONT.""" + # Currently none. RFC2217 only uses negotiation and subnegotiation. + if self.logger: + self.logger.warning("ignoring Telnet command: {!r}".format(command)) + + def _telnet_negotiate_option(self, command, option): + """Process incoming DO, DONT, WILL, WONT.""" + # check our registered telnet options and forward command to them + # they know themselves if they have to answer or not + known = False + for item in self._telnet_options: + # can have more than one match! as some options are duplicated for + # 'us' and 'them' + if item.option == option: + item.process_incoming(command) + known = True + if not known: + # handle unknown options + # only answer to positive requests and deny them + if command == WILL or command == DO: + self.telnet_send_option((DONT if command == WILL else WONT), option) + if self.logger: + self.logger.warning("rejected Telnet option: {!r}".format(option)) + + def _telnet_process_subnegotiation(self, suboption): + """Process subnegotiation, the data between IAC SB and IAC SE.""" + if suboption[0:1] == COM_PORT_OPTION: + if suboption[1:2] == SERVER_NOTIFY_LINESTATE and len(suboption) >= 3: + self._linestate = ord(suboption[2:3]) # ensure it is a number + if self.logger: + self.logger.info("NOTIFY_LINESTATE: {}".format(self._linestate)) + elif suboption[1:2] == SERVER_NOTIFY_MODEMSTATE and len(suboption) >= 3: + self._modemstate = ord(suboption[2:3]) # ensure it is a number + if self.logger: + self.logger.info("NOTIFY_MODEMSTATE: {}".format(self._modemstate)) + # update time when we think that a poll would make sense + self._modemstate_timeout.restart(0.3) + elif suboption[1:2] == FLOWCONTROL_SUSPEND: + self._remote_suspend_flow = True + elif suboption[1:2] == FLOWCONTROL_RESUME: + self._remote_suspend_flow = False + else: + for item in self._rfc2217_options.values(): + if item.ack_option == suboption[1:2]: + #~ print "processing COM_PORT_OPTION: %r" % list(suboption[1:]) + item.check_answer(bytes(suboption[2:])) + break + else: + if self.logger: + self.logger.warning("ignoring COM_PORT_OPTION: {!r}".format(suboption)) + else: + if self.logger: + self.logger.warning("ignoring subnegotiation: {!r}".format(suboption)) + + # - outgoing telnet commands and options + + def _internal_raw_write(self, data): + """internal socket write with no data escaping. used to send telnet stuff.""" + with self._write_lock: + self._socket.sendall(data) + + def telnet_send_option(self, action, option): + """Send DO, DONT, WILL, WONT.""" + self._internal_raw_write(IAC + action + option) + + def rfc2217_send_subnegotiation(self, option, value=b''): + """Subnegotiation of RFC2217 parameters.""" + value = value.replace(IAC, IAC_DOUBLED) + self._internal_raw_write(IAC + SB + COM_PORT_OPTION + option + value + IAC + SE) + + def rfc2217_send_purge(self, value): + """\ + Send purge request to the remote. + (PURGE_RECEIVE_BUFFER / PURGE_TRANSMIT_BUFFER / PURGE_BOTH_BUFFERS) + """ + item = self._rfc2217_options['purge'] + item.set(value) # transmit desired purge type + item.wait(self._network_timeout) # wait for acknowledge from the server + + def rfc2217_set_control(self, value): + """transmit change of control line to remote""" + item = self._rfc2217_options['control'] + item.set(value) # transmit desired control type + if self._ignore_set_control_answer: + # answers are ignored when option is set. compatibility mode for + # servers that answer, but not the expected one... (or no answer + # at all) i.e. sredird + time.sleep(0.1) # this helps getting the unit tests passed + else: + item.wait(self._network_timeout) # wait for acknowledge from the server + + def rfc2217_flow_server_ready(self): + """\ + check if server is ready to receive data. block for some time when + not. + """ + #~ if self._remote_suspend_flow: + #~ wait--- + + def get_modem_state(self): + """\ + get last modem state (cached value. If value is "old", request a new + one. This cache helps that we don't issue to many requests when e.g. all + status lines, one after the other is queried by the user (CTS, DSR + etc.) + """ + # active modem state polling enabled? is the value fresh enough? + if self._poll_modem_state and self._modemstate_timeout.expired(): + if self.logger: + self.logger.debug('polling modem state') + # when it is older, request an update + self.rfc2217_send_subnegotiation(NOTIFY_MODEMSTATE) + timeout = Timeout(self._network_timeout) + while not timeout.expired(): + time.sleep(0.05) # prevent 100% CPU load + # when expiration time is updated, it means that there is a new + # value + if not self._modemstate_timeout.expired(): + break + else: + if self.logger: + self.logger.warning('poll for modem state failed') + # even when there is a timeout, do not generate an error just + # return the last known value. this way we can support buggy + # servers that do not respond to polls, but send automatic + # updates. + if self._modemstate is not None: + if self.logger: + self.logger.debug('using cached modem state') + return self._modemstate + else: + # never received a notification from the server + raise SerialException("remote sends no NOTIFY_MODEMSTATE") + + +############################################################################# +# The following is code that helps implementing an RFC 2217 server. + +class PortManager(object): + """\ + This class manages the state of Telnet and RFC 2217. It needs a serial + instance and a connection to work with. Connection is expected to implement + a (thread safe) write function, that writes the string to the network. + """ + + def __init__(self, serial_port, connection, logger=None): + self.serial = serial_port + self.connection = connection + self.logger = logger + self._client_is_rfc2217 = False + + # filter state machine + self.mode = M_NORMAL + self.suboption = None + self.telnet_command = None + + # states for modem/line control events + self.modemstate_mask = 255 + self.last_modemstate = None + self.linstate_mask = 0 + + # all supported telnet options + self._telnet_options = [ + TelnetOption(self, 'ECHO', ECHO, WILL, WONT, DO, DONT, REQUESTED), + TelnetOption(self, 'we-SGA', SGA, WILL, WONT, DO, DONT, REQUESTED), + TelnetOption(self, 'they-SGA', SGA, DO, DONT, WILL, WONT, INACTIVE), + TelnetOption(self, 'we-BINARY', BINARY, WILL, WONT, DO, DONT, INACTIVE), + TelnetOption(self, 'they-BINARY', BINARY, DO, DONT, WILL, WONT, REQUESTED), + TelnetOption(self, 'we-RFC2217', COM_PORT_OPTION, WILL, WONT, DO, DONT, REQUESTED, self._client_ok), + TelnetOption(self, 'they-RFC2217', COM_PORT_OPTION, DO, DONT, WILL, WONT, INACTIVE, self._client_ok), + ] + + # negotiate Telnet/RFC2217 -> send initial requests + if self.logger: + self.logger.debug("requesting initial Telnet/RFC 2217 options") + for option in self._telnet_options: + if option.state is REQUESTED: + self.telnet_send_option(option.send_yes, option.option) + # issue 1st modem state notification + + def _client_ok(self): + """\ + callback of telnet option. It gets called when option is activated. + This one here is used to detect when the client agrees on RFC 2217. A + flag is set so that other functions like check_modem_lines know if the + client is OK. + """ + # The callback is used for we and they so if one party agrees, we're + # already happy. it seems not all servers do the negotiation correctly + # and i guess there are incorrect clients too.. so be happy if client + # answers one or the other positively. + self._client_is_rfc2217 = True + if self.logger: + self.logger.info("client accepts RFC 2217") + # this is to ensure that the client gets a notification, even if there + # was no change + self.check_modem_lines(force_notification=True) + + # - outgoing telnet commands and options + + def telnet_send_option(self, action, option): + """Send DO, DONT, WILL, WONT.""" + self.connection.write(IAC + action + option) + + def rfc2217_send_subnegotiation(self, option, value=b''): + """Subnegotiation of RFC 2217 parameters.""" + value = value.replace(IAC, IAC_DOUBLED) + self.connection.write(IAC + SB + COM_PORT_OPTION + option + value + IAC + SE) + + # - check modem lines, needs to be called periodically from user to + # establish polling + + def check_modem_lines(self, force_notification=False): + """\ + read control lines from serial port and compare the last value sent to remote. + send updates on changes. + """ + modemstate = ( + (self.serial.cts and MODEMSTATE_MASK_CTS) | + (self.serial.dsr and MODEMSTATE_MASK_DSR) | + (self.serial.ri and MODEMSTATE_MASK_RI) | + (self.serial.cd and MODEMSTATE_MASK_CD)) + # check what has changed + deltas = modemstate ^ (self.last_modemstate or 0) # when last is None -> 0 + if deltas & MODEMSTATE_MASK_CTS: + modemstate |= MODEMSTATE_MASK_CTS_CHANGE + if deltas & MODEMSTATE_MASK_DSR: + modemstate |= MODEMSTATE_MASK_DSR_CHANGE + if deltas & MODEMSTATE_MASK_RI: + modemstate |= MODEMSTATE_MASK_RI_CHANGE + if deltas & MODEMSTATE_MASK_CD: + modemstate |= MODEMSTATE_MASK_CD_CHANGE + # if new state is different and the mask allows this change, send + # notification. suppress notifications when client is not rfc2217 + if modemstate != self.last_modemstate or force_notification: + if (self._client_is_rfc2217 and (modemstate & self.modemstate_mask)) or force_notification: + self.rfc2217_send_subnegotiation( + SERVER_NOTIFY_MODEMSTATE, + to_bytes([modemstate & self.modemstate_mask])) + if self.logger: + self.logger.info("NOTIFY_MODEMSTATE: {}".format(modemstate)) + # save last state, but forget about deltas. + # otherwise it would also notify about changing deltas which is + # probably not very useful + self.last_modemstate = modemstate & 0xf0 + + # - outgoing data escaping + + def escape(self, data): + """\ + This generator function is for the user. All outgoing data has to be + properly escaped, so that no IAC character in the data stream messes up + the Telnet state machine in the server. + + socket.sendall(escape(data)) + """ + for byte in iterbytes(data): + if byte == IAC: + yield IAC + yield IAC + else: + yield byte + + # - incoming data filter + + def filter(self, data): + """\ + Handle a bunch of incoming bytes. This is a generator. It will yield + all characters not of interest for Telnet/RFC 2217. + + The idea is that the reader thread pushes data from the socket through + this filter: + + for byte in filter(socket.recv(1024)): + # do things like CR/LF conversion/whatever + # and write data to the serial port + serial.write(byte) + + (socket error handling code left as exercise for the reader) + """ + for byte in iterbytes(data): + if self.mode == M_NORMAL: + # interpret as command or as data + if byte == IAC: + self.mode = M_IAC_SEEN + else: + # store data in sub option buffer or pass it to our + # consumer depending on state + if self.suboption is not None: + self.suboption += byte + else: + yield byte + elif self.mode == M_IAC_SEEN: + if byte == IAC: + # interpret as command doubled -> insert character + # itself + if self.suboption is not None: + self.suboption += byte + else: + yield byte + self.mode = M_NORMAL + elif byte == SB: + # sub option start + self.suboption = bytearray() + self.mode = M_NORMAL + elif byte == SE: + # sub option end -> process it now + self._telnet_process_subnegotiation(bytes(self.suboption)) + self.suboption = None + self.mode = M_NORMAL + elif byte in (DO, DONT, WILL, WONT): + # negotiation + self.telnet_command = byte + self.mode = M_NEGOTIATE + else: + # other telnet commands + self._telnet_process_command(byte) + self.mode = M_NORMAL + elif self.mode == M_NEGOTIATE: # DO, DONT, WILL, WONT was received, option now following + self._telnet_negotiate_option(self.telnet_command, byte) + self.mode = M_NORMAL + + # - incoming telnet commands and options + + def _telnet_process_command(self, command): + """Process commands other than DO, DONT, WILL, WONT.""" + # Currently none. RFC2217 only uses negotiation and subnegotiation. + if self.logger: + self.logger.warning("ignoring Telnet command: {!r}".format(command)) + + def _telnet_negotiate_option(self, command, option): + """Process incoming DO, DONT, WILL, WONT.""" + # check our registered telnet options and forward command to them + # they know themselves if they have to answer or not + known = False + for item in self._telnet_options: + # can have more than one match! as some options are duplicated for + # 'us' and 'them' + if item.option == option: + item.process_incoming(command) + known = True + if not known: + # handle unknown options + # only answer to positive requests and deny them + if command == WILL or command == DO: + self.telnet_send_option((DONT if command == WILL else WONT), option) + if self.logger: + self.logger.warning("rejected Telnet option: {!r}".format(option)) + + def _telnet_process_subnegotiation(self, suboption): + """Process subnegotiation, the data between IAC SB and IAC SE.""" + if suboption[0:1] == COM_PORT_OPTION: + if self.logger: + self.logger.debug('received COM_PORT_OPTION: {!r}'.format(suboption)) + if suboption[1:2] == SET_BAUDRATE: + backup = self.serial.baudrate + try: + (baudrate,) = struct.unpack(b"!I", suboption[2:6]) + if baudrate != 0: + self.serial.baudrate = baudrate + except ValueError as e: + if self.logger: + self.logger.error("failed to set baud rate: {}".format(e)) + self.serial.baudrate = backup + else: + if self.logger: + self.logger.info("{} baud rate: {}".format('set' if baudrate else 'get', self.serial.baudrate)) + self.rfc2217_send_subnegotiation(SERVER_SET_BAUDRATE, struct.pack(b"!I", self.serial.baudrate)) + elif suboption[1:2] == SET_DATASIZE: + backup = self.serial.bytesize + try: + (datasize,) = struct.unpack(b"!B", suboption[2:3]) + if datasize != 0: + self.serial.bytesize = datasize + except ValueError as e: + if self.logger: + self.logger.error("failed to set data size: {}".format(e)) + self.serial.bytesize = backup + else: + if self.logger: + self.logger.info("{} data size: {}".format('set' if datasize else 'get', self.serial.bytesize)) + self.rfc2217_send_subnegotiation(SERVER_SET_DATASIZE, struct.pack(b"!B", self.serial.bytesize)) + elif suboption[1:2] == SET_PARITY: + backup = self.serial.parity + try: + parity = struct.unpack(b"!B", suboption[2:3])[0] + if parity != 0: + self.serial.parity = RFC2217_REVERSE_PARITY_MAP[parity] + except ValueError as e: + if self.logger: + self.logger.error("failed to set parity: {}".format(e)) + self.serial.parity = backup + else: + if self.logger: + self.logger.info("{} parity: {}".format('set' if parity else 'get', self.serial.parity)) + self.rfc2217_send_subnegotiation( + SERVER_SET_PARITY, + struct.pack(b"!B", RFC2217_PARITY_MAP[self.serial.parity])) + elif suboption[1:2] == SET_STOPSIZE: + backup = self.serial.stopbits + try: + stopbits = struct.unpack(b"!B", suboption[2:3])[0] + if stopbits != 0: + self.serial.stopbits = RFC2217_REVERSE_STOPBIT_MAP[stopbits] + except ValueError as e: + if self.logger: + self.logger.error("failed to set stop bits: {}".format(e)) + self.serial.stopbits = backup + else: + if self.logger: + self.logger.info("{} stop bits: {}".format('set' if stopbits else 'get', self.serial.stopbits)) + self.rfc2217_send_subnegotiation( + SERVER_SET_STOPSIZE, + struct.pack(b"!B", RFC2217_STOPBIT_MAP[self.serial.stopbits])) + elif suboption[1:2] == SET_CONTROL: + if suboption[2:3] == SET_CONTROL_REQ_FLOW_SETTING: + if self.serial.xonxoff: + self.rfc2217_send_subnegotiation(SERVER_SET_CONTROL, SET_CONTROL_USE_SW_FLOW_CONTROL) + elif self.serial.rtscts: + self.rfc2217_send_subnegotiation(SERVER_SET_CONTROL, SET_CONTROL_USE_HW_FLOW_CONTROL) + else: + self.rfc2217_send_subnegotiation(SERVER_SET_CONTROL, SET_CONTROL_USE_NO_FLOW_CONTROL) + elif suboption[2:3] == SET_CONTROL_USE_NO_FLOW_CONTROL: + self.serial.xonxoff = False + self.serial.rtscts = False + if self.logger: + self.logger.info("changed flow control to None") + self.rfc2217_send_subnegotiation(SERVER_SET_CONTROL, SET_CONTROL_USE_NO_FLOW_CONTROL) + elif suboption[2:3] == SET_CONTROL_USE_SW_FLOW_CONTROL: + self.serial.xonxoff = True + if self.logger: + self.logger.info("changed flow control to XON/XOFF") + self.rfc2217_send_subnegotiation(SERVER_SET_CONTROL, SET_CONTROL_USE_SW_FLOW_CONTROL) + elif suboption[2:3] == SET_CONTROL_USE_HW_FLOW_CONTROL: + self.serial.rtscts = True + if self.logger: + self.logger.info("changed flow control to RTS/CTS") + self.rfc2217_send_subnegotiation(SERVER_SET_CONTROL, SET_CONTROL_USE_HW_FLOW_CONTROL) + elif suboption[2:3] == SET_CONTROL_REQ_BREAK_STATE: + if self.logger: + self.logger.warning("requested break state - not implemented") + pass # XXX needs cached value + elif suboption[2:3] == SET_CONTROL_BREAK_ON: + self.serial.break_condition = True + if self.logger: + self.logger.info("changed BREAK to active") + self.rfc2217_send_subnegotiation(SERVER_SET_CONTROL, SET_CONTROL_BREAK_ON) + elif suboption[2:3] == SET_CONTROL_BREAK_OFF: + self.serial.break_condition = False + if self.logger: + self.logger.info("changed BREAK to inactive") + self.rfc2217_send_subnegotiation(SERVER_SET_CONTROL, SET_CONTROL_BREAK_OFF) + elif suboption[2:3] == SET_CONTROL_REQ_DTR: + if self.logger: + self.logger.warning("requested DTR state - not implemented") + pass # XXX needs cached value + elif suboption[2:3] == SET_CONTROL_DTR_ON: + self.serial.dtr = True + if self.logger: + self.logger.info("changed DTR to active") + self.rfc2217_send_subnegotiation(SERVER_SET_CONTROL, SET_CONTROL_DTR_ON) + elif suboption[2:3] == SET_CONTROL_DTR_OFF: + self.serial.dtr = False + if self.logger: + self.logger.info("changed DTR to inactive") + self.rfc2217_send_subnegotiation(SERVER_SET_CONTROL, SET_CONTROL_DTR_OFF) + elif suboption[2:3] == SET_CONTROL_REQ_RTS: + if self.logger: + self.logger.warning("requested RTS state - not implemented") + pass # XXX needs cached value + #~ self.rfc2217_send_subnegotiation(SERVER_SET_CONTROL, SET_CONTROL_RTS_ON) + elif suboption[2:3] == SET_CONTROL_RTS_ON: + self.serial.rts = True + if self.logger: + self.logger.info("changed RTS to active") + self.rfc2217_send_subnegotiation(SERVER_SET_CONTROL, SET_CONTROL_RTS_ON) + elif suboption[2:3] == SET_CONTROL_RTS_OFF: + self.serial.rts = False + if self.logger: + self.logger.info("changed RTS to inactive") + self.rfc2217_send_subnegotiation(SERVER_SET_CONTROL, SET_CONTROL_RTS_OFF) + #~ elif suboption[2:3] == SET_CONTROL_REQ_FLOW_SETTING_IN: + #~ elif suboption[2:3] == SET_CONTROL_USE_NO_FLOW_CONTROL_IN: + #~ elif suboption[2:3] == SET_CONTROL_USE_SW_FLOW_CONTOL_IN: + #~ elif suboption[2:3] == SET_CONTROL_USE_HW_FLOW_CONTOL_IN: + #~ elif suboption[2:3] == SET_CONTROL_USE_DCD_FLOW_CONTROL: + #~ elif suboption[2:3] == SET_CONTROL_USE_DTR_FLOW_CONTROL: + #~ elif suboption[2:3] == SET_CONTROL_USE_DSR_FLOW_CONTROL: + elif suboption[1:2] == NOTIFY_LINESTATE: + # client polls for current state + self.rfc2217_send_subnegotiation( + SERVER_NOTIFY_LINESTATE, + to_bytes([0])) # sorry, nothing like that implemented + elif suboption[1:2] == NOTIFY_MODEMSTATE: + if self.logger: + self.logger.info("request for modem state") + # client polls for current state + self.check_modem_lines(force_notification=True) + elif suboption[1:2] == FLOWCONTROL_SUSPEND: + if self.logger: + self.logger.info("suspend") + self._remote_suspend_flow = True + elif suboption[1:2] == FLOWCONTROL_RESUME: + if self.logger: + self.logger.info("resume") + self._remote_suspend_flow = False + elif suboption[1:2] == SET_LINESTATE_MASK: + self.linstate_mask = ord(suboption[2:3]) # ensure it is a number + if self.logger: + self.logger.info("line state mask: 0x{:02x}".format(self.linstate_mask)) + elif suboption[1:2] == SET_MODEMSTATE_MASK: + self.modemstate_mask = ord(suboption[2:3]) # ensure it is a number + if self.logger: + self.logger.info("modem state mask: 0x{:02x}".format(self.modemstate_mask)) + elif suboption[1:2] == PURGE_DATA: + if suboption[2:3] == PURGE_RECEIVE_BUFFER: + self.serial.reset_input_buffer() + if self.logger: + self.logger.info("purge in") + self.rfc2217_send_subnegotiation(SERVER_PURGE_DATA, PURGE_RECEIVE_BUFFER) + elif suboption[2:3] == PURGE_TRANSMIT_BUFFER: + self.serial.reset_output_buffer() + if self.logger: + self.logger.info("purge out") + self.rfc2217_send_subnegotiation(SERVER_PURGE_DATA, PURGE_TRANSMIT_BUFFER) + elif suboption[2:3] == PURGE_BOTH_BUFFERS: + self.serial.reset_input_buffer() + self.serial.reset_output_buffer() + if self.logger: + self.logger.info("purge both") + self.rfc2217_send_subnegotiation(SERVER_PURGE_DATA, PURGE_BOTH_BUFFERS) + else: + if self.logger: + self.logger.error("undefined PURGE_DATA: {!r}".format(list(suboption[2:]))) + else: + if self.logger: + self.logger.error("undefined COM_PORT_OPTION: {!r}".format(list(suboption[1:]))) + else: + if self.logger: + self.logger.warning("unknown subnegotiation: {!r}".format(suboption)) + + +# simple client test +if __name__ == '__main__': + import sys + s = Serial('rfc2217://localhost:7000', 115200) + sys.stdout.write('{}\n'.format(s)) + + sys.stdout.write("write...\n") + s.write(b"hello\n") + s.flush() + sys.stdout.write("read: {}\n".format(s.read(5))) + s.close() diff --git a/backend/venv/lib/python3.6/site-packages/serial/rfc2217.pyc b/backend/venv/lib/python3.6/site-packages/serial/rfc2217.pyc new file mode 100644 index 0000000..9d4727e Binary files /dev/null and b/backend/venv/lib/python3.6/site-packages/serial/rfc2217.pyc differ diff --git a/backend/venv/lib/python3.6/site-packages/serial/rs485.py b/backend/venv/lib/python3.6/site-packages/serial/rs485.py new file mode 100644 index 0000000..2939350 --- /dev/null +++ b/backend/venv/lib/python3.6/site-packages/serial/rs485.py @@ -0,0 +1,92 @@ +#!/usr/bin/env python + +# RS485 support +# +# This file is part of pySerial. https://github.com/pyserial/pyserial +# (C) 2015 Chris Liechti +# +# SPDX-License-Identifier: BSD-3-Clause + +"""\ +The settings for RS485 are stored in a dedicated object that can be applied to +serial ports (where supported). +NOTE: Some implementations may only support a subset of the settings. +""" + +import time +import serial + + +class RS485Settings(object): + def __init__( + self, + rts_level_for_tx=True, + rts_level_for_rx=False, + loopback=False, + delay_before_tx=None, + delay_before_rx=None): + self.rts_level_for_tx = rts_level_for_tx + self.rts_level_for_rx = rts_level_for_rx + self.loopback = loopback + self.delay_before_tx = delay_before_tx + self.delay_before_rx = delay_before_rx + + +class RS485(serial.Serial): + """\ + A subclass that replaces the write method with one that toggles RTS + according to the RS485 settings. + + NOTE: This may work unreliably on some serial ports (control signals not + synchronized or delayed compared to data). Using delays may be + unreliable (varying times, larger than expected) as the OS may not + support very fine grained delays (no smaller than in the order of + tens of milliseconds). + + NOTE: Some implementations support this natively. Better performance + can be expected when the native version is used. + + NOTE: The loopback property is ignored by this implementation. The actual + behavior depends on the used hardware. + + Usage: + + ser = RS485(...) + ser.rs485_mode = RS485Settings(...) + ser.write(b'hello') + """ + + def __init__(self, *args, **kwargs): + super(RS485, self).__init__(*args, **kwargs) + self._alternate_rs485_settings = None + + def write(self, b): + """Write to port, controlling RTS before and after transmitting.""" + if self._alternate_rs485_settings is not None: + # apply level for TX and optional delay + self.setRTS(self._alternate_rs485_settings.rts_level_for_tx) + if self._alternate_rs485_settings.delay_before_tx is not None: + time.sleep(self._alternate_rs485_settings.delay_before_tx) + # write and wait for data to be written + super(RS485, self).write(b) + super(RS485, self).flush() + # optional delay and apply level for RX + if self._alternate_rs485_settings.delay_before_rx is not None: + time.sleep(self._alternate_rs485_settings.delay_before_rx) + self.setRTS(self._alternate_rs485_settings.rts_level_for_rx) + else: + super(RS485, self).write(b) + + # redirect where the property stores the settings so that underlying Serial + # instance does not see them + @property + def rs485_mode(self): + """\ + Enable RS485 mode and apply new settings, set to None to disable. + See serial.rs485.RS485Settings for more info about the value. + """ + return self._alternate_rs485_settings + + @rs485_mode.setter + def rs485_mode(self, rs485_settings): + self._alternate_rs485_settings = rs485_settings diff --git a/backend/venv/lib/python3.6/site-packages/serial/rs485.pyc b/backend/venv/lib/python3.6/site-packages/serial/rs485.pyc new file mode 100644 index 0000000..09650af Binary files /dev/null and b/backend/venv/lib/python3.6/site-packages/serial/rs485.pyc differ diff --git a/backend/venv/lib/python3.6/site-packages/serial/serialcli.py b/backend/venv/lib/python3.6/site-packages/serial/serialcli.py new file mode 100644 index 0000000..0727a52 --- /dev/null +++ b/backend/venv/lib/python3.6/site-packages/serial/serialcli.py @@ -0,0 +1,251 @@ +#! python +# +# Backend for .NET/Mono (IronPython), .NET >= 2 +# +# This file is part of pySerial. https://github.com/pyserial/pyserial +# (C) 2008-2015 Chris Liechti +# +# SPDX-License-Identifier: BSD-3-Clause + +import System +import System.IO.Ports +from serial.serialutil import * + +# must invoke function with byte array, make a helper to convert strings +# to byte arrays +sab = System.Array[System.Byte] + + +def as_byte_array(string): + return sab([ord(x) for x in string]) # XXX will require adaption when run with a 3.x compatible IronPython + + +class Serial(SerialBase): + """Serial port implementation for .NET/Mono.""" + + BAUDRATES = (50, 75, 110, 134, 150, 200, 300, 600, 1200, 1800, 2400, 4800, + 9600, 19200, 38400, 57600, 115200) + + def open(self): + """\ + Open port with current settings. This may throw a SerialException + if the port cannot be opened. + """ + if self._port is None: + raise SerialException("Port must be configured before it can be used.") + if self.is_open: + raise SerialException("Port is already open.") + try: + self._port_handle = System.IO.Ports.SerialPort(self.portstr) + except Exception as msg: + self._port_handle = None + raise SerialException("could not open port %s: %s" % (self.portstr, msg)) + + # if RTS and/or DTR are not set before open, they default to True + if self._rts_state is None: + self._rts_state = True + if self._dtr_state is None: + self._dtr_state = True + + self._reconfigure_port() + self._port_handle.Open() + self.is_open = True + if not self._dsrdtr: + self._update_dtr_state() + if not self._rtscts: + self._update_rts_state() + self.reset_input_buffer() + + def _reconfigure_port(self): + """Set communication parameters on opened port.""" + if not self._port_handle: + raise SerialException("Can only operate on a valid port handle") + + #~ self._port_handle.ReceivedBytesThreshold = 1 + + if self._timeout is None: + self._port_handle.ReadTimeout = System.IO.Ports.SerialPort.InfiniteTimeout + else: + self._port_handle.ReadTimeout = int(self._timeout * 1000) + + # if self._timeout != 0 and self._interCharTimeout is not None: + # timeouts = (int(self._interCharTimeout * 1000),) + timeouts[1:] + + if self._write_timeout is None: + self._port_handle.WriteTimeout = System.IO.Ports.SerialPort.InfiniteTimeout + else: + self._port_handle.WriteTimeout = int(self._write_timeout * 1000) + + # Setup the connection info. + try: + self._port_handle.BaudRate = self._baudrate + except IOError as e: + # catch errors from illegal baudrate settings + raise ValueError(str(e)) + + if self._bytesize == FIVEBITS: + self._port_handle.DataBits = 5 + elif self._bytesize == SIXBITS: + self._port_handle.DataBits = 6 + elif self._bytesize == SEVENBITS: + self._port_handle.DataBits = 7 + elif self._bytesize == EIGHTBITS: + self._port_handle.DataBits = 8 + else: + raise ValueError("Unsupported number of data bits: %r" % self._bytesize) + + if self._parity == PARITY_NONE: + self._port_handle.Parity = getattr(System.IO.Ports.Parity, 'None') # reserved keyword in Py3k + elif self._parity == PARITY_EVEN: + self._port_handle.Parity = System.IO.Ports.Parity.Even + elif self._parity == PARITY_ODD: + self._port_handle.Parity = System.IO.Ports.Parity.Odd + elif self._parity == PARITY_MARK: + self._port_handle.Parity = System.IO.Ports.Parity.Mark + elif self._parity == PARITY_SPACE: + self._port_handle.Parity = System.IO.Ports.Parity.Space + else: + raise ValueError("Unsupported parity mode: %r" % self._parity) + + if self._stopbits == STOPBITS_ONE: + self._port_handle.StopBits = System.IO.Ports.StopBits.One + elif self._stopbits == STOPBITS_ONE_POINT_FIVE: + self._port_handle.StopBits = System.IO.Ports.StopBits.OnePointFive + elif self._stopbits == STOPBITS_TWO: + self._port_handle.StopBits = System.IO.Ports.StopBits.Two + else: + raise ValueError("Unsupported number of stop bits: %r" % self._stopbits) + + if self._rtscts and self._xonxoff: + self._port_handle.Handshake = System.IO.Ports.Handshake.RequestToSendXOnXOff + elif self._rtscts: + self._port_handle.Handshake = System.IO.Ports.Handshake.RequestToSend + elif self._xonxoff: + self._port_handle.Handshake = System.IO.Ports.Handshake.XOnXOff + else: + self._port_handle.Handshake = getattr(System.IO.Ports.Handshake, 'None') # reserved keyword in Py3k + + #~ def __del__(self): + #~ self.close() + + def close(self): + """Close port""" + if self.is_open: + if self._port_handle: + try: + self._port_handle.Close() + except System.IO.Ports.InvalidOperationException: + # ignore errors. can happen for unplugged USB serial devices + pass + self._port_handle = None + self.is_open = False + + # - - - - - - - - - - - - - - - - - - - - - - - - + + @property + def in_waiting(self): + """Return the number of characters currently in the input buffer.""" + if not self.is_open: + raise portNotOpenError + return self._port_handle.BytesToRead + + def read(self, size=1): + """\ + Read size bytes from the serial port. If a timeout is set it may + return less characters as requested. With no timeout it will block + until the requested number of bytes is read. + """ + if not self.is_open: + raise portNotOpenError + # must use single byte reads as this is the only way to read + # without applying encodings + data = bytearray() + while size: + try: + data.append(self._port_handle.ReadByte()) + except System.TimeoutException: + break + else: + size -= 1 + return bytes(data) + + def write(self, data): + """Output the given string over the serial port.""" + if not self.is_open: + raise portNotOpenError + #~ if not isinstance(data, (bytes, bytearray)): + #~ raise TypeError('expected %s or bytearray, got %s' % (bytes, type(data))) + try: + # must call overloaded method with byte array argument + # as this is the only one not applying encodings + self._port_handle.Write(as_byte_array(data), 0, len(data)) + except System.TimeoutException: + raise writeTimeoutError + return len(data) + + def reset_input_buffer(self): + """Clear input buffer, discarding all that is in the buffer.""" + if not self.is_open: + raise portNotOpenError + self._port_handle.DiscardInBuffer() + + def reset_output_buffer(self): + """\ + Clear output buffer, aborting the current output and + discarding all that is in the buffer. + """ + if not self.is_open: + raise portNotOpenError + self._port_handle.DiscardOutBuffer() + + def _update_break_state(self): + """ + Set break: Controls TXD. When active, to transmitting is possible. + """ + if not self.is_open: + raise portNotOpenError + self._port_handle.BreakState = bool(self._break_state) + + def _update_rts_state(self): + """Set terminal status line: Request To Send""" + if not self.is_open: + raise portNotOpenError + self._port_handle.RtsEnable = bool(self._rts_state) + + def _update_dtr_state(self): + """Set terminal status line: Data Terminal Ready""" + if not self.is_open: + raise portNotOpenError + self._port_handle.DtrEnable = bool(self._dtr_state) + + @property + def cts(self): + """Read terminal status line: Clear To Send""" + if not self.is_open: + raise portNotOpenError + return self._port_handle.CtsHolding + + @property + def dsr(self): + """Read terminal status line: Data Set Ready""" + if not self.is_open: + raise portNotOpenError + return self._port_handle.DsrHolding + + @property + def ri(self): + """Read terminal status line: Ring Indicator""" + if not self.is_open: + raise portNotOpenError + #~ return self._port_handle.XXX + return False # XXX an error would be better + + @property + def cd(self): + """Read terminal status line: Carrier Detect""" + if not self.is_open: + raise portNotOpenError + return self._port_handle.CDHolding + + # - - platform specific - - - - + # none diff --git a/backend/venv/lib/python3.6/site-packages/serial/serialcli.pyc b/backend/venv/lib/python3.6/site-packages/serial/serialcli.pyc new file mode 100644 index 0000000..d1caa60 Binary files /dev/null and b/backend/venv/lib/python3.6/site-packages/serial/serialcli.pyc differ diff --git a/backend/venv/lib/python3.6/site-packages/serial/serialjava.py b/backend/venv/lib/python3.6/site-packages/serial/serialjava.py new file mode 100644 index 0000000..7bd5b3e --- /dev/null +++ b/backend/venv/lib/python3.6/site-packages/serial/serialjava.py @@ -0,0 +1,249 @@ +#!jython +# +# Backend Jython with JavaComm +# +# This file is part of pySerial. https://github.com/pyserial/pyserial +# (C) 2002-2015 Chris Liechti +# +# SPDX-License-Identifier: BSD-3-Clause + +from serial.serialutil import * + + +def my_import(name): + mod = __import__(name) + components = name.split('.') + for comp in components[1:]: + mod = getattr(mod, comp) + return mod + + +def detect_java_comm(names): + """try given list of modules and return that imports""" + for name in names: + try: + mod = my_import(name) + mod.SerialPort + return mod + except (ImportError, AttributeError): + pass + raise ImportError("No Java Communications API implementation found") + + +# Java Communications API implementations +# http://mho.republika.pl/java/comm/ + +comm = detect_java_comm([ + 'javax.comm', # Sun/IBM + 'gnu.io', # RXTX +]) + + +def device(portnumber): + """Turn a port number into a device name""" + enum = comm.CommPortIdentifier.getPortIdentifiers() + ports = [] + while enum.hasMoreElements(): + el = enum.nextElement() + if el.getPortType() == comm.CommPortIdentifier.PORT_SERIAL: + ports.append(el) + return ports[portnumber].getName() + + +class Serial(SerialBase): + """\ + Serial port class, implemented with Java Communications API and + thus usable with jython and the appropriate java extension. + """ + + def open(self): + """\ + Open port with current settings. This may throw a SerialException + if the port cannot be opened. + """ + if self._port is None: + raise SerialException("Port must be configured before it can be used.") + if self.is_open: + raise SerialException("Port is already open.") + if type(self._port) == type(''): # strings are taken directly + portId = comm.CommPortIdentifier.getPortIdentifier(self._port) + else: + portId = comm.CommPortIdentifier.getPortIdentifier(device(self._port)) # numbers are transformed to a comport id obj + try: + self.sPort = portId.open("python serial module", 10) + except Exception as msg: + self.sPort = None + raise SerialException("Could not open port: %s" % msg) + self._reconfigurePort() + self._instream = self.sPort.getInputStream() + self._outstream = self.sPort.getOutputStream() + self.is_open = True + + def _reconfigurePort(self): + """Set communication parameters on opened port.""" + if not self.sPort: + raise SerialException("Can only operate on a valid port handle") + + self.sPort.enableReceiveTimeout(30) + if self._bytesize == FIVEBITS: + jdatabits = comm.SerialPort.DATABITS_5 + elif self._bytesize == SIXBITS: + jdatabits = comm.SerialPort.DATABITS_6 + elif self._bytesize == SEVENBITS: + jdatabits = comm.SerialPort.DATABITS_7 + elif self._bytesize == EIGHTBITS: + jdatabits = comm.SerialPort.DATABITS_8 + else: + raise ValueError("unsupported bytesize: %r" % self._bytesize) + + if self._stopbits == STOPBITS_ONE: + jstopbits = comm.SerialPort.STOPBITS_1 + elif self._stopbits == STOPBITS_ONE_POINT_FIVE: + jstopbits = comm.SerialPort.STOPBITS_1_5 + elif self._stopbits == STOPBITS_TWO: + jstopbits = comm.SerialPort.STOPBITS_2 + else: + raise ValueError("unsupported number of stopbits: %r" % self._stopbits) + + if self._parity == PARITY_NONE: + jparity = comm.SerialPort.PARITY_NONE + elif self._parity == PARITY_EVEN: + jparity = comm.SerialPort.PARITY_EVEN + elif self._parity == PARITY_ODD: + jparity = comm.SerialPort.PARITY_ODD + elif self._parity == PARITY_MARK: + jparity = comm.SerialPort.PARITY_MARK + elif self._parity == PARITY_SPACE: + jparity = comm.SerialPort.PARITY_SPACE + else: + raise ValueError("unsupported parity type: %r" % self._parity) + + jflowin = jflowout = 0 + if self._rtscts: + jflowin |= comm.SerialPort.FLOWCONTROL_RTSCTS_IN + jflowout |= comm.SerialPort.FLOWCONTROL_RTSCTS_OUT + if self._xonxoff: + jflowin |= comm.SerialPort.FLOWCONTROL_XONXOFF_IN + jflowout |= comm.SerialPort.FLOWCONTROL_XONXOFF_OUT + + self.sPort.setSerialPortParams(self._baudrate, jdatabits, jstopbits, jparity) + self.sPort.setFlowControlMode(jflowin | jflowout) + + if self._timeout >= 0: + self.sPort.enableReceiveTimeout(int(self._timeout*1000)) + else: + self.sPort.disableReceiveTimeout() + + def close(self): + """Close port""" + if self.is_open: + if self.sPort: + self._instream.close() + self._outstream.close() + self.sPort.close() + self.sPort = None + self.is_open = False + + # - - - - - - - - - - - - - - - - - - - - - - - - + + @property + def in_waiting(self): + """Return the number of characters currently in the input buffer.""" + if not self.sPort: + raise portNotOpenError + return self._instream.available() + + def read(self, size=1): + """\ + Read size bytes from the serial port. If a timeout is set it may + return less characters as requested. With no timeout it will block + until the requested number of bytes is read. + """ + if not self.sPort: + raise portNotOpenError + read = bytearray() + if size > 0: + while len(read) < size: + x = self._instream.read() + if x == -1: + if self.timeout >= 0: + break + else: + read.append(x) + return bytes(read) + + def write(self, data): + """Output the given string over the serial port.""" + if not self.sPort: + raise portNotOpenError + if not isinstance(data, (bytes, bytearray)): + raise TypeError('expected %s or bytearray, got %s' % (bytes, type(data))) + self._outstream.write(data) + return len(data) + + def reset_input_buffer(self): + """Clear input buffer, discarding all that is in the buffer.""" + if not self.sPort: + raise portNotOpenError + self._instream.skip(self._instream.available()) + + def reset_output_buffer(self): + """\ + Clear output buffer, aborting the current output and + discarding all that is in the buffer. + """ + if not self.sPort: + raise portNotOpenError + self._outstream.flush() + + def send_break(self, duration=0.25): + """Send break condition. Timed, returns to idle state after given duration.""" + if not self.sPort: + raise portNotOpenError + self.sPort.sendBreak(duration*1000.0) + + def _update_break_state(self): + """Set break: Controls TXD. When active, to transmitting is possible.""" + if self.fd is None: + raise portNotOpenError + raise SerialException("The _update_break_state function is not implemented in java.") + + def _update_rts_state(self): + """Set terminal status line: Request To Send""" + if not self.sPort: + raise portNotOpenError + self.sPort.setRTS(self._rts_state) + + def _update_dtr_state(self): + """Set terminal status line: Data Terminal Ready""" + if not self.sPort: + raise portNotOpenError + self.sPort.setDTR(self._dtr_state) + + @property + def cts(self): + """Read terminal status line: Clear To Send""" + if not self.sPort: + raise portNotOpenError + self.sPort.isCTS() + + @property + def dsr(self): + """Read terminal status line: Data Set Ready""" + if not self.sPort: + raise portNotOpenError + self.sPort.isDSR() + + @property + def ri(self): + """Read terminal status line: Ring Indicator""" + if not self.sPort: + raise portNotOpenError + self.sPort.isRI() + + @property + def cd(self): + """Read terminal status line: Carrier Detect""" + if not self.sPort: + raise portNotOpenError + self.sPort.isCD() diff --git a/backend/venv/lib/python3.6/site-packages/serial/serialjava.pyc b/backend/venv/lib/python3.6/site-packages/serial/serialjava.pyc new file mode 100644 index 0000000..7db4a58 Binary files /dev/null and b/backend/venv/lib/python3.6/site-packages/serial/serialjava.pyc differ diff --git a/backend/venv/lib/python3.6/site-packages/serial/serialposix.py b/backend/venv/lib/python3.6/site-packages/serial/serialposix.py new file mode 100644 index 0000000..afe5062 --- /dev/null +++ b/backend/venv/lib/python3.6/site-packages/serial/serialposix.py @@ -0,0 +1,811 @@ +#!/usr/bin/env python +# +# backend for serial IO for POSIX compatible systems, like Linux, OSX +# +# This file is part of pySerial. https://github.com/pyserial/pyserial +# (C) 2001-2016 Chris Liechti +# +# SPDX-License-Identifier: BSD-3-Clause +# +# parts based on code from Grant B. Edwards : +# ftp://ftp.visi.com/users/grante/python/PosixSerial.py +# +# references: http://www.easysw.com/~mike/serial/serial.html + +# Collection of port names (was previously used by number_to_device which was +# removed. +# - Linux /dev/ttyS%d (confirmed) +# - cygwin/win32 /dev/com%d (confirmed) +# - openbsd (OpenBSD) /dev/cua%02d +# - bsd*, freebsd* /dev/cuad%d +# - darwin (OS X) /dev/cuad%d +# - netbsd /dev/dty%02d (NetBSD 1.6 testing by Erk) +# - irix (IRIX) /dev/ttyf%d (partially tested) names depending on flow control +# - hp (HP-UX) /dev/tty%dp0 (not tested) +# - sunos (Solaris/SunOS) /dev/tty%c (letters, 'a'..'z') (confirmed) +# - aix (AIX) /dev/tty%d + + +# pylint: disable=abstract-method +import errno +import fcntl +import os +import select +import struct +import sys +import termios + +import serial +from serial.serialutil import SerialBase, SerialException, to_bytes, \ + portNotOpenError, writeTimeoutError, Timeout + + +class PlatformSpecificBase(object): + BAUDRATE_CONSTANTS = {} + + def _set_special_baudrate(self, baudrate): + raise NotImplementedError('non-standard baudrates are not supported on this platform') + + def _set_rs485_mode(self, rs485_settings): + raise NotImplementedError('RS485 not supported on this platform') + + +# some systems support an extra flag to enable the two in POSIX unsupported +# paritiy settings for MARK and SPACE +CMSPAR = 0 # default, for unsupported platforms, override below + +# try to detect the OS so that a device can be selected... +# this code block should supply a device() and set_special_baudrate() function +# for the platform +plat = sys.platform.lower() + +if plat[:5] == 'linux': # Linux (confirmed) # noqa + import array + + # extra termios flags + CMSPAR = 0o10000000000 # Use "stick" (mark/space) parity + + # baudrate ioctls + TCGETS2 = 0x802C542A + TCSETS2 = 0x402C542B + BOTHER = 0o010000 + + # RS485 ioctls + TIOCGRS485 = 0x542E + TIOCSRS485 = 0x542F + SER_RS485_ENABLED = 0b00000001 + SER_RS485_RTS_ON_SEND = 0b00000010 + SER_RS485_RTS_AFTER_SEND = 0b00000100 + SER_RS485_RX_DURING_TX = 0b00010000 + + class PlatformSpecific(PlatformSpecificBase): + BAUDRATE_CONSTANTS = { + 0: 0o000000, # hang up + 50: 0o000001, + 75: 0o000002, + 110: 0o000003, + 134: 0o000004, + 150: 0o000005, + 200: 0o000006, + 300: 0o000007, + 600: 0o000010, + 1200: 0o000011, + 1800: 0o000012, + 2400: 0o000013, + 4800: 0o000014, + 9600: 0o000015, + 19200: 0o000016, + 38400: 0o000017, + 57600: 0o010001, + 115200: 0o010002, + 230400: 0o010003, + 460800: 0o010004, + 500000: 0o010005, + 576000: 0o010006, + 921600: 0o010007, + 1000000: 0o010010, + 1152000: 0o010011, + 1500000: 0o010012, + 2000000: 0o010013, + 2500000: 0o010014, + 3000000: 0o010015, + 3500000: 0o010016, + 4000000: 0o010017 + } + + def _set_special_baudrate(self, baudrate): + # right size is 44 on x86_64, allow for some growth + buf = array.array('i', [0] * 64) + try: + # get serial_struct + fcntl.ioctl(self.fd, TCGETS2, buf) + # set custom speed + buf[2] &= ~termios.CBAUD + buf[2] |= BOTHER + buf[9] = buf[10] = baudrate + + # set serial_struct + fcntl.ioctl(self.fd, TCSETS2, buf) + except IOError as e: + raise ValueError('Failed to set custom baud rate ({}): {}'.format(baudrate, e)) + + def _set_rs485_mode(self, rs485_settings): + buf = array.array('i', [0] * 8) # flags, delaytx, delayrx, padding + try: + fcntl.ioctl(self.fd, TIOCGRS485, buf) + buf[0] |= SER_RS485_ENABLED + if rs485_settings is not None: + if rs485_settings.loopback: + buf[0] |= SER_RS485_RX_DURING_TX + else: + buf[0] &= ~SER_RS485_RX_DURING_TX + if rs485_settings.rts_level_for_tx: + buf[0] |= SER_RS485_RTS_ON_SEND + else: + buf[0] &= ~SER_RS485_RTS_ON_SEND + if rs485_settings.rts_level_for_rx: + buf[0] |= SER_RS485_RTS_AFTER_SEND + else: + buf[0] &= ~SER_RS485_RTS_AFTER_SEND + if rs485_settings.delay_before_tx is not None: + buf[1] = int(rs485_settings.delay_before_tx * 1000) + if rs485_settings.delay_before_rx is not None: + buf[2] = int(rs485_settings.delay_before_rx * 1000) + else: + buf[0] = 0 # clear SER_RS485_ENABLED + fcntl.ioctl(self.fd, TIOCSRS485, buf) + except IOError as e: + raise ValueError('Failed to set RS485 mode: {}'.format(e)) + + +elif plat == 'cygwin': # cygwin/win32 (confirmed) + + class PlatformSpecific(PlatformSpecificBase): + BAUDRATE_CONSTANTS = { + 128000: 0x01003, + 256000: 0x01005, + 500000: 0x01007, + 576000: 0x01008, + 921600: 0x01009, + 1000000: 0x0100a, + 1152000: 0x0100b, + 1500000: 0x0100c, + 2000000: 0x0100d, + 2500000: 0x0100e, + 3000000: 0x0100f + } + + +elif plat[:6] == 'darwin': # OS X + import array + IOSSIOSPEED = 0x80045402 # _IOW('T', 2, speed_t) + + class PlatformSpecific(PlatformSpecificBase): + osx_version = os.uname()[2].split('.') + # Tiger or above can support arbitrary serial speeds + if int(osx_version[0]) >= 8: + def _set_special_baudrate(self, baudrate): + # use IOKit-specific call to set up high speeds + buf = array.array('i', [baudrate]) + fcntl.ioctl(self.fd, IOSSIOSPEED, buf, 1) + +elif plat[:3] == 'bsd' or \ + plat[:7] == 'freebsd' or \ + plat[:6] == 'netbsd' or \ + plat[:7] == 'openbsd': + + class ReturnBaudrate(object): + def __getitem__(self, key): + return key + + class PlatformSpecific(PlatformSpecificBase): + # Only tested on FreeBSD: + # The baud rate may be passed in as + # a literal value. + BAUDRATE_CONSTANTS = ReturnBaudrate() + +else: + class PlatformSpecific(PlatformSpecificBase): + pass + + +# load some constants for later use. +# try to use values from termios, use defaults from linux otherwise +TIOCMGET = getattr(termios, 'TIOCMGET', 0x5415) +TIOCMBIS = getattr(termios, 'TIOCMBIS', 0x5416) +TIOCMBIC = getattr(termios, 'TIOCMBIC', 0x5417) +TIOCMSET = getattr(termios, 'TIOCMSET', 0x5418) + +# TIOCM_LE = getattr(termios, 'TIOCM_LE', 0x001) +TIOCM_DTR = getattr(termios, 'TIOCM_DTR', 0x002) +TIOCM_RTS = getattr(termios, 'TIOCM_RTS', 0x004) +# TIOCM_ST = getattr(termios, 'TIOCM_ST', 0x008) +# TIOCM_SR = getattr(termios, 'TIOCM_SR', 0x010) + +TIOCM_CTS = getattr(termios, 'TIOCM_CTS', 0x020) +TIOCM_CAR = getattr(termios, 'TIOCM_CAR', 0x040) +TIOCM_RNG = getattr(termios, 'TIOCM_RNG', 0x080) +TIOCM_DSR = getattr(termios, 'TIOCM_DSR', 0x100) +TIOCM_CD = getattr(termios, 'TIOCM_CD', TIOCM_CAR) +TIOCM_RI = getattr(termios, 'TIOCM_RI', TIOCM_RNG) +# TIOCM_OUT1 = getattr(termios, 'TIOCM_OUT1', 0x2000) +# TIOCM_OUT2 = getattr(termios, 'TIOCM_OUT2', 0x4000) +if hasattr(termios, 'TIOCINQ'): + TIOCINQ = termios.TIOCINQ +else: + TIOCINQ = getattr(termios, 'FIONREAD', 0x541B) +TIOCOUTQ = getattr(termios, 'TIOCOUTQ', 0x5411) + +TIOCM_zero_str = struct.pack('I', 0) +TIOCM_RTS_str = struct.pack('I', TIOCM_RTS) +TIOCM_DTR_str = struct.pack('I', TIOCM_DTR) + +TIOCSBRK = getattr(termios, 'TIOCSBRK', 0x5427) +TIOCCBRK = getattr(termios, 'TIOCCBRK', 0x5428) + + +class Serial(SerialBase, PlatformSpecific): + """\ + Serial port class POSIX implementation. Serial port configuration is + done with termios and fcntl. Runs on Linux and many other Un*x like + systems. + """ + + def open(self): + """\ + Open port with current settings. This may throw a SerialException + if the port cannot be opened.""" + if self._port is None: + raise SerialException("Port must be configured before it can be used.") + if self.is_open: + raise SerialException("Port is already open.") + self.fd = None + # open + try: + self.fd = os.open(self.portstr, os.O_RDWR | os.O_NOCTTY | os.O_NONBLOCK) + except OSError as msg: + self.fd = None + raise SerialException(msg.errno, "could not open port {}: {}".format(self._port, msg)) + #~ fcntl.fcntl(self.fd, fcntl.F_SETFL, 0) # set blocking + + try: + self._reconfigure_port(force_update=True) + except: + try: + os.close(self.fd) + except: + # ignore any exception when closing the port + # also to keep original exception that happened when setting up + pass + self.fd = None + raise + else: + self.is_open = True + try: + if not self._dsrdtr: + self._update_dtr_state() + if not self._rtscts: + self._update_rts_state() + except IOError as e: + if e.errno in (errno.EINVAL, errno.ENOTTY): + # ignore Invalid argument and Inappropriate ioctl + pass + else: + raise + self.reset_input_buffer() + self.pipe_abort_read_r, self.pipe_abort_read_w = os.pipe() + self.pipe_abort_write_r, self.pipe_abort_write_w = os.pipe() + fcntl.fcntl(self.pipe_abort_read_r, fcntl.F_SETFL, os.O_NONBLOCK) + fcntl.fcntl(self.pipe_abort_write_r, fcntl.F_SETFL, os.O_NONBLOCK) + + def _reconfigure_port(self, force_update=False): + """Set communication parameters on opened port.""" + if self.fd is None: + raise SerialException("Can only operate on a valid file descriptor") + + # if exclusive lock is requested, create it before we modify anything else + if self._exclusive is not None: + if self._exclusive: + try: + fcntl.flock(self.fd, fcntl.LOCK_EX | fcntl.LOCK_NB) + except IOError as msg: + raise SerialException(msg.errno, "Could not exclusively lock port {}: {}".format(self._port, msg)) + else: + fcntl.flock(self.fd, fcntl.LOCK_UN) + + custom_baud = None + + vmin = vtime = 0 # timeout is done via select + if self._inter_byte_timeout is not None: + vmin = 1 + vtime = int(self._inter_byte_timeout * 10) + try: + orig_attr = termios.tcgetattr(self.fd) + iflag, oflag, cflag, lflag, ispeed, ospeed, cc = orig_attr + except termios.error as msg: # if a port is nonexistent but has a /dev file, it'll fail here + raise SerialException("Could not configure port: {}".format(msg)) + # set up raw mode / no echo / binary + cflag |= (termios.CLOCAL | termios.CREAD) + lflag &= ~(termios.ICANON | termios.ECHO | termios.ECHOE | + termios.ECHOK | termios.ECHONL | + termios.ISIG | termios.IEXTEN) # |termios.ECHOPRT + for flag in ('ECHOCTL', 'ECHOKE'): # netbsd workaround for Erk + if hasattr(termios, flag): + lflag &= ~getattr(termios, flag) + + oflag &= ~(termios.OPOST | termios.ONLCR | termios.OCRNL) + iflag &= ~(termios.INLCR | termios.IGNCR | termios.ICRNL | termios.IGNBRK) + if hasattr(termios, 'IUCLC'): + iflag &= ~termios.IUCLC + if hasattr(termios, 'PARMRK'): + iflag &= ~termios.PARMRK + + # setup baud rate + try: + ispeed = ospeed = getattr(termios, 'B{}'.format(self._baudrate)) + except AttributeError: + try: + ispeed = ospeed = self.BAUDRATE_CONSTANTS[self._baudrate] + except KeyError: + #~ raise ValueError('Invalid baud rate: %r' % self._baudrate) + # may need custom baud rate, it isn't in our list. + ispeed = ospeed = getattr(termios, 'B38400') + try: + custom_baud = int(self._baudrate) # store for later + except ValueError: + raise ValueError('Invalid baud rate: {!r}'.format(self._baudrate)) + else: + if custom_baud < 0: + raise ValueError('Invalid baud rate: {!r}'.format(self._baudrate)) + + # setup char len + cflag &= ~termios.CSIZE + if self._bytesize == 8: + cflag |= termios.CS8 + elif self._bytesize == 7: + cflag |= termios.CS7 + elif self._bytesize == 6: + cflag |= termios.CS6 + elif self._bytesize == 5: + cflag |= termios.CS5 + else: + raise ValueError('Invalid char len: {!r}'.format(self._bytesize)) + # setup stop bits + if self._stopbits == serial.STOPBITS_ONE: + cflag &= ~(termios.CSTOPB) + elif self._stopbits == serial.STOPBITS_ONE_POINT_FIVE: + cflag |= (termios.CSTOPB) # XXX same as TWO.. there is no POSIX support for 1.5 + elif self._stopbits == serial.STOPBITS_TWO: + cflag |= (termios.CSTOPB) + else: + raise ValueError('Invalid stop bit specification: {!r}'.format(self._stopbits)) + # setup parity + iflag &= ~(termios.INPCK | termios.ISTRIP) + if self._parity == serial.PARITY_NONE: + cflag &= ~(termios.PARENB | termios.PARODD | CMSPAR) + elif self._parity == serial.PARITY_EVEN: + cflag &= ~(termios.PARODD | CMSPAR) + cflag |= (termios.PARENB) + elif self._parity == serial.PARITY_ODD: + cflag &= ~CMSPAR + cflag |= (termios.PARENB | termios.PARODD) + elif self._parity == serial.PARITY_MARK and CMSPAR: + cflag |= (termios.PARENB | CMSPAR | termios.PARODD) + elif self._parity == serial.PARITY_SPACE and CMSPAR: + cflag |= (termios.PARENB | CMSPAR) + cflag &= ~(termios.PARODD) + else: + raise ValueError('Invalid parity: {!r}'.format(self._parity)) + # setup flow control + # xonxoff + if hasattr(termios, 'IXANY'): + if self._xonxoff: + iflag |= (termios.IXON | termios.IXOFF) # |termios.IXANY) + else: + iflag &= ~(termios.IXON | termios.IXOFF | termios.IXANY) + else: + if self._xonxoff: + iflag |= (termios.IXON | termios.IXOFF) + else: + iflag &= ~(termios.IXON | termios.IXOFF) + # rtscts + if hasattr(termios, 'CRTSCTS'): + if self._rtscts: + cflag |= (termios.CRTSCTS) + else: + cflag &= ~(termios.CRTSCTS) + elif hasattr(termios, 'CNEW_RTSCTS'): # try it with alternate constant name + if self._rtscts: + cflag |= (termios.CNEW_RTSCTS) + else: + cflag &= ~(termios.CNEW_RTSCTS) + # XXX should there be a warning if setting up rtscts (and xonxoff etc) fails?? + + # buffer + # vmin "minimal number of characters to be read. 0 for non blocking" + if vmin < 0 or vmin > 255: + raise ValueError('Invalid vmin: {!r}'.format(vmin)) + cc[termios.VMIN] = vmin + # vtime + if vtime < 0 or vtime > 255: + raise ValueError('Invalid vtime: {!r}'.format(vtime)) + cc[termios.VTIME] = vtime + # activate settings + if force_update or [iflag, oflag, cflag, lflag, ispeed, ospeed, cc] != orig_attr: + termios.tcsetattr( + self.fd, + termios.TCSANOW, + [iflag, oflag, cflag, lflag, ispeed, ospeed, cc]) + + # apply custom baud rate, if any + if custom_baud is not None: + self._set_special_baudrate(custom_baud) + + if self._rs485_mode is not None: + self._set_rs485_mode(self._rs485_mode) + + def close(self): + """Close port""" + if self.is_open: + if self.fd is not None: + os.close(self.fd) + self.fd = None + os.close(self.pipe_abort_read_w) + os.close(self.pipe_abort_read_r) + os.close(self.pipe_abort_write_w) + os.close(self.pipe_abort_write_r) + self.pipe_abort_read_r, self.pipe_abort_read_w = None, None + self.pipe_abort_write_r, self.pipe_abort_write_w = None, None + self.is_open = False + + # - - - - - - - - - - - - - - - - - - - - - - - - + + @property + def in_waiting(self): + """Return the number of bytes currently in the input buffer.""" + #~ s = fcntl.ioctl(self.fd, termios.FIONREAD, TIOCM_zero_str) + s = fcntl.ioctl(self.fd, TIOCINQ, TIOCM_zero_str) + return struct.unpack('I', s)[0] + + # select based implementation, proved to work on many systems + def read(self, size=1): + """\ + Read size bytes from the serial port. If a timeout is set it may + return less characters as requested. With no timeout it will block + until the requested number of bytes is read. + """ + if not self.is_open: + raise portNotOpenError + read = bytearray() + timeout = Timeout(self._timeout) + while len(read) < size: + try: + ready, _, _ = select.select([self.fd, self.pipe_abort_read_r], [], [], timeout.time_left()) + if self.pipe_abort_read_r in ready: + os.read(self.pipe_abort_read_r, 1000) + break + # If select was used with a timeout, and the timeout occurs, it + # returns with empty lists -> thus abort read operation. + # For timeout == 0 (non-blocking operation) also abort when + # there is nothing to read. + if not ready: + break # timeout + buf = os.read(self.fd, size - len(read)) + # read should always return some data as select reported it was + # ready to read when we get to this point. + if not buf: + # Disconnected devices, at least on Linux, show the + # behavior that they are always ready to read immediately + # but reading returns nothing. + raise SerialException( + 'device reports readiness to read but returned no data ' + '(device disconnected or multiple access on port?)') + read.extend(buf) + except OSError as e: + # this is for Python 3.x where select.error is a subclass of + # OSError ignore BlockingIOErrors and EINTR. other errors are shown + # https://www.python.org/dev/peps/pep-0475. + if e.errno not in (errno.EAGAIN, errno.EALREADY, errno.EWOULDBLOCK, errno.EINPROGRESS, errno.EINTR): + raise SerialException('read failed: {}'.format(e)) + except select.error as e: + # this is for Python 2.x + # ignore BlockingIOErrors and EINTR. all errors are shown + # see also http://www.python.org/dev/peps/pep-3151/#select + if e[0] not in (errno.EAGAIN, errno.EALREADY, errno.EWOULDBLOCK, errno.EINPROGRESS, errno.EINTR): + raise SerialException('read failed: {}'.format(e)) + if timeout.expired(): + break + return bytes(read) + + def cancel_read(self): + if self.is_open: + os.write(self.pipe_abort_read_w, b"x") + + def cancel_write(self): + if self.is_open: + os.write(self.pipe_abort_write_w, b"x") + + def write(self, data): + """Output the given byte string over the serial port.""" + if not self.is_open: + raise portNotOpenError + d = to_bytes(data) + tx_len = length = len(d) + timeout = Timeout(self._write_timeout) + while tx_len > 0: + try: + n = os.write(self.fd, d) + if timeout.is_non_blocking: + # Zero timeout indicates non-blocking - simply return the + # number of bytes of data actually written + return n + elif not timeout.is_infinite: + # when timeout is set, use select to wait for being ready + # with the time left as timeout + if timeout.expired(): + raise writeTimeoutError + abort, ready, _ = select.select([self.pipe_abort_write_r], [self.fd], [], timeout.time_left()) + if abort: + os.read(self.pipe_abort_write_r, 1000) + break + if not ready: + raise writeTimeoutError + else: + assert timeout.time_left() is None + # wait for write operation + abort, ready, _ = select.select([self.pipe_abort_write_r], [self.fd], [], None) + if abort: + os.read(self.pipe_abort_write_r, 1) + break + if not ready: + raise SerialException('write failed (select)') + d = d[n:] + tx_len -= n + except SerialException: + raise + except OSError as e: + # this is for Python 3.x where select.error is a subclass of + # OSError ignore BlockingIOErrors and EINTR. other errors are shown + # https://www.python.org/dev/peps/pep-0475. + if e.errno not in (errno.EAGAIN, errno.EALREADY, errno.EWOULDBLOCK, errno.EINPROGRESS, errno.EINTR): + raise SerialException('write failed: {}'.format(e)) + except select.error as e: + # this is for Python 2.x + # ignore BlockingIOErrors and EINTR. all errors are shown + # see also http://www.python.org/dev/peps/pep-3151/#select + if e[0] not in (errno.EAGAIN, errno.EALREADY, errno.EWOULDBLOCK, errno.EINPROGRESS, errno.EINTR): + raise SerialException('write failed: {}'.format(e)) + if not timeout.is_non_blocking and timeout.expired(): + raise writeTimeoutError + return length - len(d) + + def flush(self): + """\ + Flush of file like objects. In this case, wait until all data + is written. + """ + if not self.is_open: + raise portNotOpenError + termios.tcdrain(self.fd) + + def reset_input_buffer(self): + """Clear input buffer, discarding all that is in the buffer.""" + if not self.is_open: + raise portNotOpenError + termios.tcflush(self.fd, termios.TCIFLUSH) + + def reset_output_buffer(self): + """\ + Clear output buffer, aborting the current output and discarding all + that is in the buffer. + """ + if not self.is_open: + raise portNotOpenError + termios.tcflush(self.fd, termios.TCOFLUSH) + + def send_break(self, duration=0.25): + """\ + Send break condition. Timed, returns to idle state after given + duration. + """ + if not self.is_open: + raise portNotOpenError + termios.tcsendbreak(self.fd, int(duration / 0.25)) + + def _update_break_state(self): + """\ + Set break: Controls TXD. When active, no transmitting is possible. + """ + if self._break_state: + fcntl.ioctl(self.fd, TIOCSBRK) + else: + fcntl.ioctl(self.fd, TIOCCBRK) + + def _update_rts_state(self): + """Set terminal status line: Request To Send""" + if self._rts_state: + fcntl.ioctl(self.fd, TIOCMBIS, TIOCM_RTS_str) + else: + fcntl.ioctl(self.fd, TIOCMBIC, TIOCM_RTS_str) + + def _update_dtr_state(self): + """Set terminal status line: Data Terminal Ready""" + if self._dtr_state: + fcntl.ioctl(self.fd, TIOCMBIS, TIOCM_DTR_str) + else: + fcntl.ioctl(self.fd, TIOCMBIC, TIOCM_DTR_str) + + @property + def cts(self): + """Read terminal status line: Clear To Send""" + if not self.is_open: + raise portNotOpenError + s = fcntl.ioctl(self.fd, TIOCMGET, TIOCM_zero_str) + return struct.unpack('I', s)[0] & TIOCM_CTS != 0 + + @property + def dsr(self): + """Read terminal status line: Data Set Ready""" + if not self.is_open: + raise portNotOpenError + s = fcntl.ioctl(self.fd, TIOCMGET, TIOCM_zero_str) + return struct.unpack('I', s)[0] & TIOCM_DSR != 0 + + @property + def ri(self): + """Read terminal status line: Ring Indicator""" + if not self.is_open: + raise portNotOpenError + s = fcntl.ioctl(self.fd, TIOCMGET, TIOCM_zero_str) + return struct.unpack('I', s)[0] & TIOCM_RI != 0 + + @property + def cd(self): + """Read terminal status line: Carrier Detect""" + if not self.is_open: + raise portNotOpenError + s = fcntl.ioctl(self.fd, TIOCMGET, TIOCM_zero_str) + return struct.unpack('I', s)[0] & TIOCM_CD != 0 + + # - - platform specific - - - - + + @property + def out_waiting(self): + """Return the number of bytes currently in the output buffer.""" + #~ s = fcntl.ioctl(self.fd, termios.FIONREAD, TIOCM_zero_str) + s = fcntl.ioctl(self.fd, TIOCOUTQ, TIOCM_zero_str) + return struct.unpack('I', s)[0] + + def fileno(self): + """\ + For easier use of the serial port instance with select. + WARNING: this function is not portable to different platforms! + """ + if not self.is_open: + raise portNotOpenError + return self.fd + + def set_input_flow_control(self, enable=True): + """\ + Manually control flow - when software flow control is enabled. + This will send XON (true) or XOFF (false) to the other device. + WARNING: this function is not portable to different platforms! + """ + if not self.is_open: + raise portNotOpenError + if enable: + termios.tcflow(self.fd, termios.TCION) + else: + termios.tcflow(self.fd, termios.TCIOFF) + + def set_output_flow_control(self, enable=True): + """\ + Manually control flow of outgoing data - when hardware or software flow + control is enabled. + WARNING: this function is not portable to different platforms! + """ + if not self.is_open: + raise portNotOpenError + if enable: + termios.tcflow(self.fd, termios.TCOON) + else: + termios.tcflow(self.fd, termios.TCOOFF) + + def nonblocking(self): + """DEPRECATED - has no use""" + import warnings + warnings.warn("nonblocking() has no effect, already nonblocking", DeprecationWarning) + + +class PosixPollSerial(Serial): + """\ + Poll based read implementation. Not all systems support poll properly. + However this one has better handling of errors, such as a device + disconnecting while it's in use (e.g. USB-serial unplugged). + """ + + def read(self, size=1): + """\ + Read size bytes from the serial port. If a timeout is set it may + return less characters as requested. With no timeout it will block + until the requested number of bytes is read. + """ + if not self.is_open: + raise portNotOpenError + read = bytearray() + poll = select.poll() + poll.register(self.fd, select.POLLIN | select.POLLERR | select.POLLHUP | select.POLLNVAL) + if size > 0: + while len(read) < size: + # print "\tread(): size",size, "have", len(read) #debug + # wait until device becomes ready to read (or something fails) + for fd, event in poll.poll(self._timeout * 1000): + if event & (select.POLLERR | select.POLLHUP | select.POLLNVAL): + raise SerialException('device reports error (poll)') + # we don't care if it is select.POLLIN or timeout, that's + # handled below + buf = os.read(self.fd, size - len(read)) + read.extend(buf) + if ((self._timeout is not None and self._timeout >= 0) or + (self._inter_byte_timeout is not None and self._inter_byte_timeout > 0)) and not buf: + break # early abort on timeout + return bytes(read) + + +class VTIMESerial(Serial): + """\ + Implement timeout using vtime of tty device instead of using select. + This means that no inter character timeout can be specified and that + the error handling is degraded. + + Overall timeout is disabled when inter-character timeout is used. + """ + + def _reconfigure_port(self, force_update=True): + """Set communication parameters on opened port.""" + super(VTIMESerial, self)._reconfigure_port() + fcntl.fcntl(self.fd, fcntl.F_SETFL, 0) # clear O_NONBLOCK + + if self._inter_byte_timeout is not None: + vmin = 1 + vtime = int(self._inter_byte_timeout * 10) + elif self._timeout is None: + vmin = 1 + vtime = 0 + else: + vmin = 0 + vtime = int(self._timeout * 10) + try: + orig_attr = termios.tcgetattr(self.fd) + iflag, oflag, cflag, lflag, ispeed, ospeed, cc = orig_attr + except termios.error as msg: # if a port is nonexistent but has a /dev file, it'll fail here + raise serial.SerialException("Could not configure port: {}".format(msg)) + + if vtime < 0 or vtime > 255: + raise ValueError('Invalid vtime: {!r}'.format(vtime)) + cc[termios.VTIME] = vtime + cc[termios.VMIN] = vmin + + termios.tcsetattr( + self.fd, + termios.TCSANOW, + [iflag, oflag, cflag, lflag, ispeed, ospeed, cc]) + + def read(self, size=1): + """\ + Read size bytes from the serial port. If a timeout is set it may + return less characters as requested. With no timeout it will block + until the requested number of bytes is read. + """ + if not self.is_open: + raise portNotOpenError + read = bytearray() + while len(read) < size: + buf = os.read(self.fd, size - len(read)) + if not buf: + break + read.extend(buf) + return bytes(read) + + # hack to make hasattr return false + cancel_read = property() diff --git a/backend/venv/lib/python3.6/site-packages/serial/serialposix.pyc b/backend/venv/lib/python3.6/site-packages/serial/serialposix.pyc new file mode 100644 index 0000000..f008f2b Binary files /dev/null and b/backend/venv/lib/python3.6/site-packages/serial/serialposix.pyc differ diff --git a/backend/venv/lib/python3.6/site-packages/serial/serialutil.py b/backend/venv/lib/python3.6/site-packages/serial/serialutil.py new file mode 100644 index 0000000..7d51752 --- /dev/null +++ b/backend/venv/lib/python3.6/site-packages/serial/serialutil.py @@ -0,0 +1,693 @@ +#! python +# +# Base class and support functions used by various backends. +# +# This file is part of pySerial. https://github.com/pyserial/pyserial +# (C) 2001-2016 Chris Liechti +# +# SPDX-License-Identifier: BSD-3-Clause + +import io +import time + +# ``memoryview`` was introduced in Python 2.7 and ``bytes(some_memoryview)`` +# isn't returning the contents (very unfortunate). Therefore we need special +# cases and test for it. Ensure that there is a ``memoryview`` object for older +# Python versions. This is easier than making every test dependent on its +# existence. +try: + memoryview +except (NameError, AttributeError): + # implementation does not matter as we do not really use it. + # it just must not inherit from something else we might care for. + class memoryview(object): # pylint: disable=redefined-builtin,invalid-name + pass + +try: + unicode +except (NameError, AttributeError): + unicode = str # for Python 3, pylint: disable=redefined-builtin,invalid-name + +try: + basestring +except (NameError, AttributeError): + basestring = (str,) # for Python 3, pylint: disable=redefined-builtin,invalid-name + + +# "for byte in data" fails for python3 as it returns ints instead of bytes +def iterbytes(b): + """Iterate over bytes, returning bytes instead of ints (python3)""" + if isinstance(b, memoryview): + b = b.tobytes() + i = 0 + while True: + a = b[i:i + 1] + i += 1 + if a: + yield a + else: + break + + +# all Python versions prior 3.x convert ``str([17])`` to '[17]' instead of '\x11' +# so a simple ``bytes(sequence)`` doesn't work for all versions +def to_bytes(seq): + """convert a sequence to a bytes type""" + if isinstance(seq, bytes): + return seq + elif isinstance(seq, bytearray): + return bytes(seq) + elif isinstance(seq, memoryview): + return seq.tobytes() + elif isinstance(seq, unicode): + raise TypeError('unicode strings are not supported, please encode to bytes: {!r}'.format(seq)) + else: + # handle list of integers and bytes (one or more items) for Python 2 and 3 + return bytes(bytearray(seq)) + + +# create control bytes +XON = to_bytes([17]) +XOFF = to_bytes([19]) + +CR = to_bytes([13]) +LF = to_bytes([10]) + + +PARITY_NONE, PARITY_EVEN, PARITY_ODD, PARITY_MARK, PARITY_SPACE = 'N', 'E', 'O', 'M', 'S' +STOPBITS_ONE, STOPBITS_ONE_POINT_FIVE, STOPBITS_TWO = (1, 1.5, 2) +FIVEBITS, SIXBITS, SEVENBITS, EIGHTBITS = (5, 6, 7, 8) + +PARITY_NAMES = { + PARITY_NONE: 'None', + PARITY_EVEN: 'Even', + PARITY_ODD: 'Odd', + PARITY_MARK: 'Mark', + PARITY_SPACE: 'Space', +} + + +class SerialException(IOError): + """Base class for serial port related exceptions.""" + + +class SerialTimeoutException(SerialException): + """Write timeouts give an exception""" + + +writeTimeoutError = SerialTimeoutException('Write timeout') +portNotOpenError = SerialException('Attempting to use a port that is not open') + + +class Timeout(object): + """\ + Abstraction for timeout operations. Using time.monotonic() if available + or time.time() in all other cases. + + The class can also be initialized with 0 or None, in order to support + non-blocking and fully blocking I/O operations. The attributes + is_non_blocking and is_infinite are set accordingly. + """ + if hasattr(time, 'monotonic'): + # Timeout implementation with time.monotonic(). This function is only + # supported by Python 3.3 and above. It returns a time in seconds + # (float) just as time.time(), but is not affected by system clock + # adjustments. + TIME = time.monotonic + else: + # Timeout implementation with time.time(). This is compatible with all + # Python versions but has issues if the clock is adjusted while the + # timeout is running. + TIME = time.time + + def __init__(self, duration): + """Initialize a timeout with given duration""" + self.is_infinite = (duration is None) + self.is_non_blocking = (duration == 0) + self.duration = duration + if duration is not None: + self.target_time = self.TIME() + duration + else: + self.target_time = None + + def expired(self): + """Return a boolean, telling if the timeout has expired""" + return self.target_time is not None and self.time_left() <= 0 + + def time_left(self): + """Return how many seconds are left until the timeout expires""" + if self.is_non_blocking: + return 0 + elif self.is_infinite: + return None + else: + delta = self.target_time - self.TIME() + if delta > self.duration: + # clock jumped, recalculate + self.target_time = self.TIME() + self.duration + return self.duration + else: + return max(0, delta) + + def restart(self, duration): + """\ + Restart a timeout, only supported if a timeout was already set up + before. + """ + self.duration = duration + self.target_time = self.TIME() + duration + + +class SerialBase(io.RawIOBase): + """\ + Serial port base class. Provides __init__ function and properties to + get/set port settings. + """ + + # default values, may be overridden in subclasses that do not support all values + BAUDRATES = (50, 75, 110, 134, 150, 200, 300, 600, 1200, 1800, 2400, 4800, + 9600, 19200, 38400, 57600, 115200, 230400, 460800, 500000, + 576000, 921600, 1000000, 1152000, 1500000, 2000000, 2500000, + 3000000, 3500000, 4000000) + BYTESIZES = (FIVEBITS, SIXBITS, SEVENBITS, EIGHTBITS) + PARITIES = (PARITY_NONE, PARITY_EVEN, PARITY_ODD, PARITY_MARK, PARITY_SPACE) + STOPBITS = (STOPBITS_ONE, STOPBITS_ONE_POINT_FIVE, STOPBITS_TWO) + + def __init__(self, + port=None, + baudrate=9600, + bytesize=EIGHTBITS, + parity=PARITY_NONE, + stopbits=STOPBITS_ONE, + timeout=None, + xonxoff=False, + rtscts=False, + write_timeout=None, + dsrdtr=False, + inter_byte_timeout=None, + exclusive=None, + **kwargs): + """\ + Initialize comm port object. If a "port" is given, then the port will be + opened immediately. Otherwise a Serial port object in closed state + is returned. + """ + + self.is_open = False + self.portstr = None + self.name = None + # correct values are assigned below through properties + self._port = None + self._baudrate = None + self._bytesize = None + self._parity = None + self._stopbits = None + self._timeout = None + self._write_timeout = None + self._xonxoff = None + self._rtscts = None + self._dsrdtr = None + self._inter_byte_timeout = None + self._rs485_mode = None # disabled by default + self._rts_state = True + self._dtr_state = True + self._break_state = False + self._exclusive = None + + # assign values using get/set methods using the properties feature + self.port = port + self.baudrate = baudrate + self.bytesize = bytesize + self.parity = parity + self.stopbits = stopbits + self.timeout = timeout + self.write_timeout = write_timeout + self.xonxoff = xonxoff + self.rtscts = rtscts + self.dsrdtr = dsrdtr + self.inter_byte_timeout = inter_byte_timeout + self.exclusive = exclusive + + # watch for backward compatible kwargs + if 'writeTimeout' in kwargs: + self.write_timeout = kwargs.pop('writeTimeout') + if 'interCharTimeout' in kwargs: + self.inter_byte_timeout = kwargs.pop('interCharTimeout') + if kwargs: + raise ValueError('unexpected keyword arguments: {!r}'.format(kwargs)) + + if port is not None: + self.open() + + # - - - - - - - - - - - - - - - - - - - - - - - - + + # to be implemented by subclasses: + # def open(self): + # def close(self): + + # - - - - - - - - - - - - - - - - - - - - - - - - + + @property + def port(self): + """\ + Get the current port setting. The value that was passed on init or using + setPort() is passed back. + """ + return self._port + + @port.setter + def port(self, port): + """\ + Change the port. + """ + if port is not None and not isinstance(port, basestring): + raise ValueError('"port" must be None or a string, not {}'.format(type(port))) + was_open = self.is_open + if was_open: + self.close() + self.portstr = port + self._port = port + self.name = self.portstr + if was_open: + self.open() + + @property + def baudrate(self): + """Get the current baud rate setting.""" + return self._baudrate + + @baudrate.setter + def baudrate(self, baudrate): + """\ + Change baud rate. It raises a ValueError if the port is open and the + baud rate is not possible. If the port is closed, then the value is + accepted and the exception is raised when the port is opened. + """ + try: + b = int(baudrate) + except TypeError: + raise ValueError("Not a valid baudrate: {!r}".format(baudrate)) + else: + if b < 0: + raise ValueError("Not a valid baudrate: {!r}".format(baudrate)) + self._baudrate = b + if self.is_open: + self._reconfigure_port() + + @property + def bytesize(self): + """Get the current byte size setting.""" + return self._bytesize + + @bytesize.setter + def bytesize(self, bytesize): + """Change byte size.""" + if bytesize not in self.BYTESIZES: + raise ValueError("Not a valid byte size: {!r}".format(bytesize)) + self._bytesize = bytesize + if self.is_open: + self._reconfigure_port() + + @property + def exclusive(self): + """Get the current exclusive access setting.""" + return self._exclusive + + @exclusive.setter + def exclusive(self, exclusive): + """Change the exclusive access setting.""" + self._exclusive = exclusive + if self.is_open: + self._reconfigure_port() + + @property + def parity(self): + """Get the current parity setting.""" + return self._parity + + @parity.setter + def parity(self, parity): + """Change parity setting.""" + if parity not in self.PARITIES: + raise ValueError("Not a valid parity: {!r}".format(parity)) + self._parity = parity + if self.is_open: + self._reconfigure_port() + + @property + def stopbits(self): + """Get the current stop bits setting.""" + return self._stopbits + + @stopbits.setter + def stopbits(self, stopbits): + """Change stop bits size.""" + if stopbits not in self.STOPBITS: + raise ValueError("Not a valid stop bit size: {!r}".format(stopbits)) + self._stopbits = stopbits + if self.is_open: + self._reconfigure_port() + + @property + def timeout(self): + """Get the current timeout setting.""" + return self._timeout + + @timeout.setter + def timeout(self, timeout): + """Change timeout setting.""" + if timeout is not None: + try: + timeout + 1 # test if it's a number, will throw a TypeError if not... + except TypeError: + raise ValueError("Not a valid timeout: {!r}".format(timeout)) + if timeout < 0: + raise ValueError("Not a valid timeout: {!r}".format(timeout)) + self._timeout = timeout + if self.is_open: + self._reconfigure_port() + + @property + def write_timeout(self): + """Get the current timeout setting.""" + return self._write_timeout + + @write_timeout.setter + def write_timeout(self, timeout): + """Change timeout setting.""" + if timeout is not None: + if timeout < 0: + raise ValueError("Not a valid timeout: {!r}".format(timeout)) + try: + timeout + 1 # test if it's a number, will throw a TypeError if not... + except TypeError: + raise ValueError("Not a valid timeout: {!r}".format(timeout)) + + self._write_timeout = timeout + if self.is_open: + self._reconfigure_port() + + @property + def inter_byte_timeout(self): + """Get the current inter-character timeout setting.""" + return self._inter_byte_timeout + + @inter_byte_timeout.setter + def inter_byte_timeout(self, ic_timeout): + """Change inter-byte timeout setting.""" + if ic_timeout is not None: + if ic_timeout < 0: + raise ValueError("Not a valid timeout: {!r}".format(ic_timeout)) + try: + ic_timeout + 1 # test if it's a number, will throw a TypeError if not... + except TypeError: + raise ValueError("Not a valid timeout: {!r}".format(ic_timeout)) + + self._inter_byte_timeout = ic_timeout + if self.is_open: + self._reconfigure_port() + + @property + def xonxoff(self): + """Get the current XON/XOFF setting.""" + return self._xonxoff + + @xonxoff.setter + def xonxoff(self, xonxoff): + """Change XON/XOFF setting.""" + self._xonxoff = xonxoff + if self.is_open: + self._reconfigure_port() + + @property + def rtscts(self): + """Get the current RTS/CTS flow control setting.""" + return self._rtscts + + @rtscts.setter + def rtscts(self, rtscts): + """Change RTS/CTS flow control setting.""" + self._rtscts = rtscts + if self.is_open: + self._reconfigure_port() + + @property + def dsrdtr(self): + """Get the current DSR/DTR flow control setting.""" + return self._dsrdtr + + @dsrdtr.setter + def dsrdtr(self, dsrdtr=None): + """Change DsrDtr flow control setting.""" + if dsrdtr is None: + # if not set, keep backwards compatibility and follow rtscts setting + self._dsrdtr = self._rtscts + else: + # if defined independently, follow its value + self._dsrdtr = dsrdtr + if self.is_open: + self._reconfigure_port() + + @property + def rts(self): + return self._rts_state + + @rts.setter + def rts(self, value): + self._rts_state = value + if self.is_open: + self._update_rts_state() + + @property + def dtr(self): + return self._dtr_state + + @dtr.setter + def dtr(self, value): + self._dtr_state = value + if self.is_open: + self._update_dtr_state() + + @property + def break_condition(self): + return self._break_state + + @break_condition.setter + def break_condition(self, value): + self._break_state = value + if self.is_open: + self._update_break_state() + + # - - - - - - - - - - - - - - - - - - - - - - - - + # functions useful for RS-485 adapters + + @property + def rs485_mode(self): + """\ + Enable RS485 mode and apply new settings, set to None to disable. + See serial.rs485.RS485Settings for more info about the value. + """ + return self._rs485_mode + + @rs485_mode.setter + def rs485_mode(self, rs485_settings): + self._rs485_mode = rs485_settings + if self.is_open: + self._reconfigure_port() + + # - - - - - - - - - - - - - - - - - - - - - - - - + + _SAVED_SETTINGS = ('baudrate', 'bytesize', 'parity', 'stopbits', 'xonxoff', + 'dsrdtr', 'rtscts', 'timeout', 'write_timeout', + 'inter_byte_timeout') + + def get_settings(self): + """\ + Get current port settings as a dictionary. For use with + apply_settings(). + """ + return dict([(key, getattr(self, '_' + key)) for key in self._SAVED_SETTINGS]) + + def apply_settings(self, d): + """\ + Apply stored settings from a dictionary returned from + get_settings(). It's allowed to delete keys from the dictionary. These + values will simply left unchanged. + """ + for key in self._SAVED_SETTINGS: + if key in d and d[key] != getattr(self, '_' + key): # check against internal "_" value + setattr(self, key, d[key]) # set non "_" value to use properties write function + + # - - - - - - - - - - - - - - - - - - - - - - - - + + def __repr__(self): + """String representation of the current port settings and its state.""" + return '{name}(port={p.portstr!r}, ' \ + 'baudrate={p.baudrate!r}, bytesize={p.bytesize!r}, parity={p.parity!r}, ' \ + 'stopbits={p.stopbits!r}, timeout={p.timeout!r}, xonxoff={p.xonxoff!r}, ' \ + 'rtscts={p.rtscts!r}, dsrdtr={p.dsrdtr!r})'.format( + name=self.__class__.__name__, id=id(self), p=self) + + # - - - - - - - - - - - - - - - - - - - - - - - - + # compatibility with io library + # pylint: disable=invalid-name,missing-docstring + + def readable(self): + return True + + def writable(self): + return True + + def seekable(self): + return False + + def readinto(self, b): + data = self.read(len(b)) + n = len(data) + try: + b[:n] = data + except TypeError as err: + import array + if not isinstance(b, array.array): + raise err + b[:n] = array.array('b', data) + return n + + # - - - - - - - - - - - - - - - - - - - - - - - - + # context manager + + def __enter__(self): + if not self.is_open: + self.open() + return self + + def __exit__(self, *args, **kwargs): + self.close() + + # - - - - - - - - - - - - - - - - - - - - - - - - + + def send_break(self, duration=0.25): + """\ + Send break condition. Timed, returns to idle state after given + duration. + """ + if not self.is_open: + raise portNotOpenError + self.break_condition = True + time.sleep(duration) + self.break_condition = False + + # - - - - - - - - - - - - - - - - - - - - - - - - + # backwards compatibility / deprecated functions + + def flushInput(self): + self.reset_input_buffer() + + def flushOutput(self): + self.reset_output_buffer() + + def inWaiting(self): + return self.in_waiting + + def sendBreak(self, duration=0.25): + self.send_break(duration) + + def setRTS(self, value=1): + self.rts = value + + def setDTR(self, value=1): + self.dtr = value + + def getCTS(self): + return self.cts + + def getDSR(self): + return self.dsr + + def getRI(self): + return self.ri + + def getCD(self): + return self.cd + + def setPort(self, port): + self.port = port + + @property + def writeTimeout(self): + return self.write_timeout + + @writeTimeout.setter + def writeTimeout(self, timeout): + self.write_timeout = timeout + + @property + def interCharTimeout(self): + return self.inter_byte_timeout + + @interCharTimeout.setter + def interCharTimeout(self, interCharTimeout): + self.inter_byte_timeout = interCharTimeout + + def getSettingsDict(self): + return self.get_settings() + + def applySettingsDict(self, d): + self.apply_settings(d) + + def isOpen(self): + return self.is_open + + # - - - - - - - - - - - - - - - - - - - - - - - - + # additional functionality + + def read_all(self): + """\ + Read all bytes currently available in the buffer of the OS. + """ + return self.read(self.in_waiting) + + def read_until(self, terminator=LF, size=None): + """\ + Read until a termination sequence is found ('\n' by default), the size + is exceeded or until timeout occurs. + """ + lenterm = len(terminator) + line = bytearray() + timeout = Timeout(self._timeout) + while True: + c = self.read(1) + if c: + line += c + if line[-lenterm:] == terminator: + break + if size is not None and len(line) >= size: + break + else: + break + if timeout.expired(): + break + return bytes(line) + + def iread_until(self, *args, **kwargs): + """\ + Read lines, implemented as generator. It will raise StopIteration on + timeout (empty read). + """ + while True: + line = self.read_until(*args, **kwargs) + if not line: + break + yield line + + +# - - - - - - - - - - - - - - - - - - - - - - - - - +if __name__ == '__main__': + import sys + s = SerialBase() + sys.stdout.write('port name: {}\n'.format(s.name)) + sys.stdout.write('baud rates: {}\n'.format(s.BAUDRATES)) + sys.stdout.write('byte sizes: {}\n'.format(s.BYTESIZES)) + sys.stdout.write('parities: {}\n'.format(s.PARITIES)) + sys.stdout.write('stop bits: {}\n'.format(s.STOPBITS)) + sys.stdout.write('{}\n'.format(s)) diff --git a/backend/venv/lib/python3.6/site-packages/serial/serialutil.pyc b/backend/venv/lib/python3.6/site-packages/serial/serialutil.pyc new file mode 100644 index 0000000..3800ba7 Binary files /dev/null and b/backend/venv/lib/python3.6/site-packages/serial/serialutil.pyc differ diff --git a/backend/venv/lib/python3.6/site-packages/serial/serialwin32.py b/backend/venv/lib/python3.6/site-packages/serial/serialwin32.py new file mode 100644 index 0000000..7b88999 --- /dev/null +++ b/backend/venv/lib/python3.6/site-packages/serial/serialwin32.py @@ -0,0 +1,475 @@ +#! python +# +# backend for Windows ("win32" incl. 32/64 bit support) +# +# (C) 2001-2015 Chris Liechti +# +# This file is part of pySerial. https://github.com/pyserial/pyserial +# SPDX-License-Identifier: BSD-3-Clause +# +# Initial patch to use ctypes by Giovanni Bajo + +# pylint: disable=invalid-name,too-few-public-methods +import ctypes +import time +from serial import win32 + +import serial +from serial.serialutil import SerialBase, SerialException, to_bytes, portNotOpenError, writeTimeoutError + + +class Serial(SerialBase): + """Serial port implementation for Win32 based on ctypes.""" + + BAUDRATES = (50, 75, 110, 134, 150, 200, 300, 600, 1200, 1800, 2400, 4800, + 9600, 19200, 38400, 57600, 115200) + + def __init__(self, *args, **kwargs): + self._port_handle = None + self._overlapped_read = None + self._overlapped_write = None + super(Serial, self).__init__(*args, **kwargs) + + def open(self): + """\ + Open port with current settings. This may throw a SerialException + if the port cannot be opened. + """ + if self._port is None: + raise SerialException("Port must be configured before it can be used.") + if self.is_open: + raise SerialException("Port is already open.") + # the "\\.\COMx" format is required for devices other than COM1-COM8 + # not all versions of windows seem to support this properly + # so that the first few ports are used with the DOS device name + port = self.name + try: + if port.upper().startswith('COM') and int(port[3:]) > 8: + port = '\\\\.\\' + port + except ValueError: + # for like COMnotanumber + pass + self._port_handle = win32.CreateFile( + port, + win32.GENERIC_READ | win32.GENERIC_WRITE, + 0, # exclusive access + None, # no security + win32.OPEN_EXISTING, + win32.FILE_ATTRIBUTE_NORMAL | win32.FILE_FLAG_OVERLAPPED, + 0) + if self._port_handle == win32.INVALID_HANDLE_VALUE: + self._port_handle = None # 'cause __del__ is called anyway + raise SerialException("could not open port {!r}: {!r}".format(self.portstr, ctypes.WinError())) + + try: + self._overlapped_read = win32.OVERLAPPED() + self._overlapped_read.hEvent = win32.CreateEvent(None, 1, 0, None) + self._overlapped_write = win32.OVERLAPPED() + #~ self._overlapped_write.hEvent = win32.CreateEvent(None, 1, 0, None) + self._overlapped_write.hEvent = win32.CreateEvent(None, 0, 0, None) + + # Setup a 4k buffer + win32.SetupComm(self._port_handle, 4096, 4096) + + # Save original timeout values: + self._orgTimeouts = win32.COMMTIMEOUTS() + win32.GetCommTimeouts(self._port_handle, ctypes.byref(self._orgTimeouts)) + + self._reconfigure_port() + + # Clear buffers: + # Remove anything that was there + win32.PurgeComm( + self._port_handle, + win32.PURGE_TXCLEAR | win32.PURGE_TXABORT | + win32.PURGE_RXCLEAR | win32.PURGE_RXABORT) + except: + try: + self._close() + except: + # ignore any exception when closing the port + # also to keep original exception that happened when setting up + pass + self._port_handle = None + raise + else: + self.is_open = True + + def _reconfigure_port(self): + """Set communication parameters on opened port.""" + if not self._port_handle: + raise SerialException("Can only operate on a valid port handle") + + # Set Windows timeout values + # timeouts is a tuple with the following items: + # (ReadIntervalTimeout,ReadTotalTimeoutMultiplier, + # ReadTotalTimeoutConstant,WriteTotalTimeoutMultiplier, + # WriteTotalTimeoutConstant) + timeouts = win32.COMMTIMEOUTS() + if self._timeout is None: + pass # default of all zeros is OK + elif self._timeout == 0: + timeouts.ReadIntervalTimeout = win32.MAXDWORD + else: + timeouts.ReadTotalTimeoutConstant = max(int(self._timeout * 1000), 1) + if self._timeout != 0 and self._inter_byte_timeout is not None: + timeouts.ReadIntervalTimeout = max(int(self._inter_byte_timeout * 1000), 1) + + if self._write_timeout is None: + pass + elif self._write_timeout == 0: + timeouts.WriteTotalTimeoutConstant = win32.MAXDWORD + else: + timeouts.WriteTotalTimeoutConstant = max(int(self._write_timeout * 1000), 1) + win32.SetCommTimeouts(self._port_handle, ctypes.byref(timeouts)) + + win32.SetCommMask(self._port_handle, win32.EV_ERR) + + # Setup the connection info. + # Get state and modify it: + comDCB = win32.DCB() + win32.GetCommState(self._port_handle, ctypes.byref(comDCB)) + comDCB.BaudRate = self._baudrate + + if self._bytesize == serial.FIVEBITS: + comDCB.ByteSize = 5 + elif self._bytesize == serial.SIXBITS: + comDCB.ByteSize = 6 + elif self._bytesize == serial.SEVENBITS: + comDCB.ByteSize = 7 + elif self._bytesize == serial.EIGHTBITS: + comDCB.ByteSize = 8 + else: + raise ValueError("Unsupported number of data bits: {!r}".format(self._bytesize)) + + if self._parity == serial.PARITY_NONE: + comDCB.Parity = win32.NOPARITY + comDCB.fParity = 0 # Disable Parity Check + elif self._parity == serial.PARITY_EVEN: + comDCB.Parity = win32.EVENPARITY + comDCB.fParity = 1 # Enable Parity Check + elif self._parity == serial.PARITY_ODD: + comDCB.Parity = win32.ODDPARITY + comDCB.fParity = 1 # Enable Parity Check + elif self._parity == serial.PARITY_MARK: + comDCB.Parity = win32.MARKPARITY + comDCB.fParity = 1 # Enable Parity Check + elif self._parity == serial.PARITY_SPACE: + comDCB.Parity = win32.SPACEPARITY + comDCB.fParity = 1 # Enable Parity Check + else: + raise ValueError("Unsupported parity mode: {!r}".format(self._parity)) + + if self._stopbits == serial.STOPBITS_ONE: + comDCB.StopBits = win32.ONESTOPBIT + elif self._stopbits == serial.STOPBITS_ONE_POINT_FIVE: + comDCB.StopBits = win32.ONE5STOPBITS + elif self._stopbits == serial.STOPBITS_TWO: + comDCB.StopBits = win32.TWOSTOPBITS + else: + raise ValueError("Unsupported number of stop bits: {!r}".format(self._stopbits)) + + comDCB.fBinary = 1 # Enable Binary Transmission + # Char. w/ Parity-Err are replaced with 0xff (if fErrorChar is set to TRUE) + if self._rs485_mode is None: + if self._rtscts: + comDCB.fRtsControl = win32.RTS_CONTROL_HANDSHAKE + else: + comDCB.fRtsControl = win32.RTS_CONTROL_ENABLE if self._rts_state else win32.RTS_CONTROL_DISABLE + comDCB.fOutxCtsFlow = self._rtscts + else: + # checks for unsupported settings + # XXX verify if platform really does not have a setting for those + if not self._rs485_mode.rts_level_for_tx: + raise ValueError( + 'Unsupported value for RS485Settings.rts_level_for_tx: {!r}'.format( + self._rs485_mode.rts_level_for_tx,)) + if self._rs485_mode.rts_level_for_rx: + raise ValueError( + 'Unsupported value for RS485Settings.rts_level_for_rx: {!r}'.format( + self._rs485_mode.rts_level_for_rx,)) + if self._rs485_mode.delay_before_tx is not None: + raise ValueError( + 'Unsupported value for RS485Settings.delay_before_tx: {!r}'.format( + self._rs485_mode.delay_before_tx,)) + if self._rs485_mode.delay_before_rx is not None: + raise ValueError( + 'Unsupported value for RS485Settings.delay_before_rx: {!r}'.format( + self._rs485_mode.delay_before_rx,)) + if self._rs485_mode.loopback: + raise ValueError( + 'Unsupported value for RS485Settings.loopback: {!r}'.format( + self._rs485_mode.loopback,)) + comDCB.fRtsControl = win32.RTS_CONTROL_TOGGLE + comDCB.fOutxCtsFlow = 0 + + if self._dsrdtr: + comDCB.fDtrControl = win32.DTR_CONTROL_HANDSHAKE + else: + comDCB.fDtrControl = win32.DTR_CONTROL_ENABLE if self._dtr_state else win32.DTR_CONTROL_DISABLE + comDCB.fOutxDsrFlow = self._dsrdtr + comDCB.fOutX = self._xonxoff + comDCB.fInX = self._xonxoff + comDCB.fNull = 0 + comDCB.fErrorChar = 0 + comDCB.fAbortOnError = 0 + comDCB.XonChar = serial.XON + comDCB.XoffChar = serial.XOFF + + if not win32.SetCommState(self._port_handle, ctypes.byref(comDCB)): + raise SerialException( + 'Cannot configure port, something went wrong. ' + 'Original message: {!r}'.format(ctypes.WinError())) + + #~ def __del__(self): + #~ self.close() + + def _close(self): + """internal close port helper""" + if self._port_handle is not None: + # Restore original timeout values: + win32.SetCommTimeouts(self._port_handle, self._orgTimeouts) + if self._overlapped_read is not None: + self.cancel_read() + win32.CloseHandle(self._overlapped_read.hEvent) + self._overlapped_read = None + if self._overlapped_write is not None: + self.cancel_write() + win32.CloseHandle(self._overlapped_write.hEvent) + self._overlapped_write = None + win32.CloseHandle(self._port_handle) + self._port_handle = None + + def close(self): + """Close port""" + if self.is_open: + self._close() + self.is_open = False + + # - - - - - - - - - - - - - - - - - - - - - - - - + + @property + def in_waiting(self): + """Return the number of bytes currently in the input buffer.""" + flags = win32.DWORD() + comstat = win32.COMSTAT() + if not win32.ClearCommError(self._port_handle, ctypes.byref(flags), ctypes.byref(comstat)): + raise SerialException("ClearCommError failed ({!r})".format(ctypes.WinError())) + return comstat.cbInQue + + def read(self, size=1): + """\ + Read size bytes from the serial port. If a timeout is set it may + return less characters as requested. With no timeout it will block + until the requested number of bytes is read. + """ + if not self.is_open: + raise portNotOpenError + if size > 0: + win32.ResetEvent(self._overlapped_read.hEvent) + flags = win32.DWORD() + comstat = win32.COMSTAT() + if not win32.ClearCommError(self._port_handle, ctypes.byref(flags), ctypes.byref(comstat)): + raise SerialException("ClearCommError failed ({!r})".format(ctypes.WinError())) + n = min(comstat.cbInQue, size) if self.timeout == 0 else size + if n > 0: + buf = ctypes.create_string_buffer(n) + rc = win32.DWORD() + read_ok = win32.ReadFile( + self._port_handle, + buf, + n, + ctypes.byref(rc), + ctypes.byref(self._overlapped_read)) + if not read_ok and win32.GetLastError() not in (win32.ERROR_SUCCESS, win32.ERROR_IO_PENDING): + raise SerialException("ReadFile failed ({!r})".format(ctypes.WinError())) + result_ok = win32.GetOverlappedResult( + self._port_handle, + ctypes.byref(self._overlapped_read), + ctypes.byref(rc), + True) + if not result_ok: + if win32.GetLastError() != win32.ERROR_OPERATION_ABORTED: + raise SerialException("GetOverlappedResult failed ({!r})".format(ctypes.WinError())) + read = buf.raw[:rc.value] + else: + read = bytes() + else: + read = bytes() + return bytes(read) + + def write(self, data): + """Output the given byte string over the serial port.""" + if not self.is_open: + raise portNotOpenError + #~ if not isinstance(data, (bytes, bytearray)): + #~ raise TypeError('expected %s or bytearray, got %s' % (bytes, type(data))) + # convert data (needed in case of memoryview instance: Py 3.1 io lib), ctypes doesn't like memoryview + data = to_bytes(data) + if data: + #~ win32event.ResetEvent(self._overlapped_write.hEvent) + n = win32.DWORD() + success = win32.WriteFile(self._port_handle, data, len(data), ctypes.byref(n), self._overlapped_write) + if self._write_timeout != 0: # if blocking (None) or w/ write timeout (>0) + if not success and win32.GetLastError() not in (win32.ERROR_SUCCESS, win32.ERROR_IO_PENDING): + raise SerialException("WriteFile failed ({!r})".format(ctypes.WinError())) + + # Wait for the write to complete. + #~ win32.WaitForSingleObject(self._overlapped_write.hEvent, win32.INFINITE) + win32.GetOverlappedResult(self._port_handle, self._overlapped_write, ctypes.byref(n), True) + if win32.GetLastError() == win32.ERROR_OPERATION_ABORTED: + return n.value # canceled IO is no error + if n.value != len(data): + raise writeTimeoutError + return n.value + else: + errorcode = win32.ERROR_SUCCESS if success else win32.GetLastError() + if errorcode in (win32.ERROR_INVALID_USER_BUFFER, win32.ERROR_NOT_ENOUGH_MEMORY, + win32.ERROR_OPERATION_ABORTED): + return 0 + elif errorcode in (win32.ERROR_SUCCESS, win32.ERROR_IO_PENDING): + # no info on true length provided by OS function in async mode + return len(data) + else: + raise SerialException("WriteFile failed ({!r})".format(ctypes.WinError())) + else: + return 0 + + def flush(self): + """\ + Flush of file like objects. In this case, wait until all data + is written. + """ + while self.out_waiting: + time.sleep(0.05) + # XXX could also use WaitCommEvent with mask EV_TXEMPTY, but it would + # require overlapped IO and it's also only possible to set a single mask + # on the port--- + + def reset_input_buffer(self): + """Clear input buffer, discarding all that is in the buffer.""" + if not self.is_open: + raise portNotOpenError + win32.PurgeComm(self._port_handle, win32.PURGE_RXCLEAR | win32.PURGE_RXABORT) + + def reset_output_buffer(self): + """\ + Clear output buffer, aborting the current output and discarding all + that is in the buffer. + """ + if not self.is_open: + raise portNotOpenError + win32.PurgeComm(self._port_handle, win32.PURGE_TXCLEAR | win32.PURGE_TXABORT) + + def _update_break_state(self): + """Set break: Controls TXD. When active, to transmitting is possible.""" + if not self.is_open: + raise portNotOpenError + if self._break_state: + win32.SetCommBreak(self._port_handle) + else: + win32.ClearCommBreak(self._port_handle) + + def _update_rts_state(self): + """Set terminal status line: Request To Send""" + if self._rts_state: + win32.EscapeCommFunction(self._port_handle, win32.SETRTS) + else: + win32.EscapeCommFunction(self._port_handle, win32.CLRRTS) + + def _update_dtr_state(self): + """Set terminal status line: Data Terminal Ready""" + if self._dtr_state: + win32.EscapeCommFunction(self._port_handle, win32.SETDTR) + else: + win32.EscapeCommFunction(self._port_handle, win32.CLRDTR) + + def _GetCommModemStatus(self): + if not self.is_open: + raise portNotOpenError + stat = win32.DWORD() + win32.GetCommModemStatus(self._port_handle, ctypes.byref(stat)) + return stat.value + + @property + def cts(self): + """Read terminal status line: Clear To Send""" + return win32.MS_CTS_ON & self._GetCommModemStatus() != 0 + + @property + def dsr(self): + """Read terminal status line: Data Set Ready""" + return win32.MS_DSR_ON & self._GetCommModemStatus() != 0 + + @property + def ri(self): + """Read terminal status line: Ring Indicator""" + return win32.MS_RING_ON & self._GetCommModemStatus() != 0 + + @property + def cd(self): + """Read terminal status line: Carrier Detect""" + return win32.MS_RLSD_ON & self._GetCommModemStatus() != 0 + + # - - platform specific - - - - + + def set_buffer_size(self, rx_size=4096, tx_size=None): + """\ + Recommend a buffer size to the driver (device driver can ignore this + value). Must be called before the port is opened. + """ + if tx_size is None: + tx_size = rx_size + win32.SetupComm(self._port_handle, rx_size, tx_size) + + def set_output_flow_control(self, enable=True): + """\ + Manually control flow - when software flow control is enabled. + This will do the same as if XON (true) or XOFF (false) are received + from the other device and control the transmission accordingly. + WARNING: this function is not portable to different platforms! + """ + if not self.is_open: + raise portNotOpenError + if enable: + win32.EscapeCommFunction(self._port_handle, win32.SETXON) + else: + win32.EscapeCommFunction(self._port_handle, win32.SETXOFF) + + @property + def out_waiting(self): + """Return how many bytes the in the outgoing buffer""" + flags = win32.DWORD() + comstat = win32.COMSTAT() + if not win32.ClearCommError(self._port_handle, ctypes.byref(flags), ctypes.byref(comstat)): + raise SerialException("ClearCommError failed ({!r})".format(ctypes.WinError())) + return comstat.cbOutQue + + def _cancel_overlapped_io(self, overlapped): + """Cancel a blocking read operation, may be called from other thread""" + # check if read operation is pending + rc = win32.DWORD() + err = win32.GetOverlappedResult( + self._port_handle, + ctypes.byref(overlapped), + ctypes.byref(rc), + False) + if not err and win32.GetLastError() in (win32.ERROR_IO_PENDING, win32.ERROR_IO_INCOMPLETE): + # cancel, ignoring any errors (e.g. it may just have finished on its own) + win32.CancelIoEx(self._port_handle, overlapped) + + def cancel_read(self): + """Cancel a blocking read operation, may be called from other thread""" + self._cancel_overlapped_io(self._overlapped_read) + + def cancel_write(self): + """Cancel a blocking write operation, may be called from other thread""" + self._cancel_overlapped_io(self._overlapped_write) + + @SerialBase.exclusive.setter + def exclusive(self, exclusive): + """Change the exclusive access setting.""" + if exclusive is not None and not exclusive: + raise ValueError('win32 only supports exclusive access (not: {})'.format(exclusive)) + else: + serial.SerialBase.exclusive.__set__(self, exclusive) diff --git a/backend/venv/lib/python3.6/site-packages/serial/serialwin32.pyc b/backend/venv/lib/python3.6/site-packages/serial/serialwin32.pyc new file mode 100644 index 0000000..f994e45 Binary files /dev/null and b/backend/venv/lib/python3.6/site-packages/serial/serialwin32.pyc differ diff --git a/backend/venv/lib/python3.6/site-packages/serial/test.py b/backend/venv/lib/python3.6/site-packages/serial/test.py deleted file mode 100644 index 198ed6a..0000000 --- a/backend/venv/lib/python3.6/site-packages/serial/test.py +++ /dev/null @@ -1,304 +0,0 @@ -from __future__ import nested_scopes, generators, division, absolute_import, with_statement, \ - print_function, unicode_literals -from .utilities.compatibility import backport - -backport() # noqa - -from future.utils import native_str - -from serial.utilities import qualified_name, calling_function_qualified_name -from serial.abc.model import Model - -from warnings import warn - -import collections -import json as _json -from itertools import chain - -import yaml as _yaml - -import serial - -try: - import typing -except ImportError as e: - typing = None - - -def _object_discrepancies(a, b): - # type: (serial.model.Object, serial.model.Object) -> dict - discrepancies = {} - a_properties = set(serial.meta.read(a).properties.keys()) - b_properties = set(serial.meta.read(b).properties.keys()) - for property_ in a_properties | b_properties: - try: - a_value = getattr(a, property_) - except AttributeError: - a_value = None - try: - b_value = getattr(b, property_) - except AttributeError: - b_value = None - if a_value != b_value: - discrepancies[property_] = (a_value, b_value) - return discrepancies - - -def json( - model_instance, # type: serial.model.Model - raise_validation_errors=True, # type: bool -): - # type: (...) -> None - model( - model_instance=model_instance, - format_='json', - raise_validation_errors=raise_validation_errors - ) - - -def yaml( - model_instance, # type: serial.model.Model - raise_validation_errors=True, # type: bool -): - # type: (...) -> None - model( - model_instance=model_instance, - format_='yaml', - raise_validation_errors=raise_validation_errors - ) - - -def xml( - model_instance, # type: serial.model.Model - raise_validation_errors=True, # type: bool -): - # type: (...) -> None - raise NotImplementedError() - # model( - # model_instance=model_instance, - # format_='xml', - # raise_validation_errors=raise_validation_errors - # ) - - -def model( - model_instance, # type: serial.model.Model - format_, # type: str - raise_validation_errors=True, # type: bool -): - # type: (...) -> None - """ - Tests an instance of a `serial.model.Model` sub-class. - - Parameters: - - - model_instance (serial.model.Model): - - An instance of a `serial.model.Model` sub-class. - - - format_ (str): - - The serialization format being tested: 'json', 'yaml' or 'xml'. - - - raise_validation_errors (bool): - - The function `serial.model.validate` verifies that all required attributes are present, as well as any - additional validations implemented using the model's validation hooks `after_validate` and/or - `before_validate`. - - - If `True`, errors resulting from `serial.model.validate` are raised. - - - If `False`, errors resulting from `serial.model.validate` are expressed only as warnings. - """ - if not isinstance(model_instance, Model): - - value_representation = repr(model_instance) - - raise TypeError( - '`%s` requires an instance of `%s` for the parameter `model_instance`, not%s' % ( - calling_function_qualified_name(), - qualified_name(Model), - ( - (':\n%s' if '\n' in value_representation else ' `%s`') % - value_representation - ) - ) - ) - - serial.meta.format_(model_instance, format_) - - if isinstance(model_instance, serial.model.Object): - - errors = serial.marshal.validate(model_instance, raise_errors=raise_validation_errors) - - if errors: - warn('\n' + '\n'.join(errors)) - - model_type = type(model_instance) - - string = str(model_instance) - - assert string != '' - - reloaded_model_instance = model_type(string) - qualified_model_name = qualified_name(type(model_instance)) - - try: - - assert model_instance == reloaded_model_instance - - except AssertionError as e: - - sa = serial.marshal.serialize(model_instance) - sb = serial.marshal.serialize(reloaded_model_instance) - - ra = ''.join(l.strip() for l in repr(model_instance).split('\n')) - rb = ''.join(l.strip() for l in repr(reloaded_model_instance).split('\n')) - - message = [ - 'Discrepancies were found between the instance of `%s` provided and ' % qualified_model_name + - 'a serialized/deserialized clone:' - ] - - if sa != sb: - - message.append( - '\n :\n\n %s\n !=\n %s' % ( - sa, - sb - ) - ) - - if ra != rb: - - message.append( - '\n %s\n !=\n %s\n' % ( - ra, - rb - ) - ) - - for k, a_b in _object_discrepancies(model_instance, reloaded_model_instance).items(): - - a, b = a_b - - assert a != b - - sa = serial.marshal.serialize(a) - sb = serial.marshal.serialize(b) - - if sa != sb: - - message.append( - '\n %s().%s:\n\n %s\n %s\n %s' % ( - qualified_model_name, - k, - sa, - '==' if sa == sb else '!=', - sb - ) - ) - - ra = ''.join(l.strip() for l in repr(a).split('\n')) - rb = ''.join(l.strip() for l in repr(b).split('\n')) - - if ra != rb: - - message.append( - '\n %s\n %s\n %s' % ( - ra, - '==' if ra == rb else '!=', - rb - ) - ) - - e.args = tuple( - chain( - (e.args[0] + '\n' + '\n'.join(message) if e.args else '\n'.join(message),), - e.args[1:] if e.args else tuple() - ) - ) - - raise e - - reloaded_string = str(reloaded_model_instance) - - try: - assert string == reloaded_string - except AssertionError as e: - m = '\n%s\n!=\n%s' % (string, reloaded_string) - if e.args: - e.args = tuple(chain( - (e.args[0] + '\n' + m,), - e.args[1:] - )) - else: - e.args = (m,) - raise e - - if format_ == 'json': - reloaded_marshalled_data = _json.loads( - string, - object_hook=collections.OrderedDict, - object_pairs_hook=collections.OrderedDict - ) - elif format_ == 'yaml': - reloaded_marshalled_data = _yaml.load(string) - elif format_ == 'xml': - raise NotImplementedError() - else: - format_representation = repr(format_) - raise ValueError( - 'Valid serialization types for parameter `format_` are "json", "yaml", or "xml'", not" + ( - ( - ':\n%s' if '\n' in format_representation else ' %s.' - ) % format_representation - ) - ) - - keys = set() - - for property_name, property in serial.meta.read(model_instance).properties.items(): - - keys.add(property.name or property_name) - property_value = getattr(model_instance, property_name) - - if isinstance(property_value, Model) or ( - hasattr(property_value, '__iter__') and - (not isinstance(property_value, (str, native_str, bytes))) - ): - model(property_value, format_=format_, raise_validation_errors=raise_validation_errors) - - for k in reloaded_marshalled_data.keys(): - - if k not in keys: - raise KeyError( - '"%s" not found in serialized/re-deserialized data: %s' % ( - k, - string - ) - ) - - elif isinstance(model_instance, serial.model.Array): - - serial.marshal.validate(model_instance) - - for item in model_instance: - - if isinstance(item, Model) or ( - hasattr(item, '__iter__') and - (not isinstance(item, (str, native_str, bytes))) - ): - model(item, format_=format_, raise_validation_errors=raise_validation_errors) - - elif isinstance(model_instance, serial.model.Dictionary): - - serial.marshal.validate(model_instance) - - for key, value in model_instance.items(): - - if isinstance(value, Model) or ( - hasattr(value, '__iter__') and - (not isinstance(value, (str, native_str, bytes))) - ): - model(value, format_=format_, raise_validation_errors=raise_validation_errors) diff --git a/backend/venv/lib/python3.6/site-packages/serial/threaded/__init__.py b/backend/venv/lib/python3.6/site-packages/serial/threaded/__init__.py new file mode 100644 index 0000000..74b6924 --- /dev/null +++ b/backend/venv/lib/python3.6/site-packages/serial/threaded/__init__.py @@ -0,0 +1,295 @@ +#!/usr/bin/env python3 +# +# Working with threading and pySerial +# +# This file is part of pySerial. https://github.com/pyserial/pyserial +# (C) 2015-2016 Chris Liechti +# +# SPDX-License-Identifier: BSD-3-Clause +"""\ +Support threading with serial ports. +""" +import serial +import threading + + +class Protocol(object): + """\ + Protocol as used by the ReaderThread. This base class provides empty + implementations of all methods. + """ + + def connection_made(self, transport): + """Called when reader thread is started""" + + def data_received(self, data): + """Called with snippets received from the serial port""" + + def connection_lost(self, exc): + """\ + Called when the serial port is closed or the reader loop terminated + otherwise. + """ + if isinstance(exc, Exception): + raise exc + + +class Packetizer(Protocol): + """ + Read binary packets from serial port. Packets are expected to be terminated + with a TERMINATOR byte (null byte by default). + + The class also keeps track of the transport. + """ + + TERMINATOR = b'\0' + + def __init__(self): + self.buffer = bytearray() + self.transport = None + + def connection_made(self, transport): + """Store transport""" + self.transport = transport + + def connection_lost(self, exc): + """Forget transport""" + self.transport = None + super(Packetizer, self).connection_lost(exc) + + def data_received(self, data): + """Buffer received data, find TERMINATOR, call handle_packet""" + self.buffer.extend(data) + while self.TERMINATOR in self.buffer: + packet, self.buffer = self.buffer.split(self.TERMINATOR, 1) + self.handle_packet(packet) + + def handle_packet(self, packet): + """Process packets - to be overridden by subclassing""" + raise NotImplementedError('please implement functionality in handle_packet') + + +class FramedPacket(Protocol): + """ + Read binary packets. Packets are expected to have a start and stop marker. + + The class also keeps track of the transport. + """ + + START = b'(' + STOP = b')' + + def __init__(self): + self.packet = bytearray() + self.in_packet = False + self.transport = None + + def connection_made(self, transport): + """Store transport""" + self.transport = transport + + def connection_lost(self, exc): + """Forget transport""" + self.transport = None + self.in_packet = False + del self.packet[:] + super(FramedPacket, self).connection_lost(exc) + + def data_received(self, data): + """Find data enclosed in START/STOP, call handle_packet""" + for byte in serial.iterbytes(data): + if byte == self.START: + self.in_packet = True + elif byte == self.STOP: + self.in_packet = False + self.handle_packet(bytes(self.packet)) # make read-only copy + del self.packet[:] + elif self.in_packet: + self.packet.extend(byte) + else: + self.handle_out_of_packet_data(byte) + + def handle_packet(self, packet): + """Process packets - to be overridden by subclassing""" + raise NotImplementedError('please implement functionality in handle_packet') + + def handle_out_of_packet_data(self, data): + """Process data that is received outside of packets""" + pass + + +class LineReader(Packetizer): + """ + Read and write (Unicode) lines from/to serial port. + The encoding is applied. + """ + + TERMINATOR = b'\r\n' + ENCODING = 'utf-8' + UNICODE_HANDLING = 'replace' + + def handle_packet(self, packet): + self.handle_line(packet.decode(self.ENCODING, self.UNICODE_HANDLING)) + + def handle_line(self, line): + """Process one line - to be overridden by subclassing""" + raise NotImplementedError('please implement functionality in handle_line') + + def write_line(self, text): + """ + Write text to the transport. ``text`` is a Unicode string and the encoding + is applied before sending ans also the newline is append. + """ + # + is not the best choice but bytes does not support % or .format in py3 and we want a single write call + self.transport.write(text.encode(self.ENCODING, self.UNICODE_HANDLING) + self.TERMINATOR) + + +class ReaderThread(threading.Thread): + """\ + Implement a serial port read loop and dispatch to a Protocol instance (like + the asyncio.Protocol) but do it with threads. + + Calls to close() will close the serial port but it is also possible to just + stop() this thread and continue the serial port instance otherwise. + """ + + def __init__(self, serial_instance, protocol_factory): + """\ + Initialize thread. + + Note that the serial_instance' timeout is set to one second! + Other settings are not changed. + """ + super(ReaderThread, self).__init__() + self.daemon = True + self.serial = serial_instance + self.protocol_factory = protocol_factory + self.alive = True + self._lock = threading.Lock() + self._connection_made = threading.Event() + self.protocol = None + + def stop(self): + """Stop the reader thread""" + self.alive = False + if hasattr(self.serial, 'cancel_read'): + self.serial.cancel_read() + self.join(2) + + def run(self): + """Reader loop""" + if not hasattr(self.serial, 'cancel_read'): + self.serial.timeout = 1 + self.protocol = self.protocol_factory() + try: + self.protocol.connection_made(self) + except Exception as e: + self.alive = False + self.protocol.connection_lost(e) + self._connection_made.set() + return + error = None + self._connection_made.set() + while self.alive and self.serial.is_open: + try: + # read all that is there or wait for one byte (blocking) + data = self.serial.read(self.serial.in_waiting or 1) + except serial.SerialException as e: + # probably some I/O problem such as disconnected USB serial + # adapters -> exit + error = e + break + else: + if data: + # make a separated try-except for called used code + try: + self.protocol.data_received(data) + except Exception as e: + error = e + break + self.alive = False + self.protocol.connection_lost(error) + self.protocol = None + + def write(self, data): + """Thread safe writing (uses lock)""" + with self._lock: + self.serial.write(data) + + def close(self): + """Close the serial port and exit reader thread (uses lock)""" + # use the lock to let other threads finish writing + with self._lock: + # first stop reading, so that closing can be done on idle port + self.stop() + self.serial.close() + + def connect(self): + """ + Wait until connection is set up and return the transport and protocol + instances. + """ + if self.alive: + self._connection_made.wait() + if not self.alive: + raise RuntimeError('connection_lost already called') + return (self, self.protocol) + else: + raise RuntimeError('already stopped') + + # - - context manager, returns protocol + + def __enter__(self): + """\ + Enter context handler. May raise RuntimeError in case the connection + could not be created. + """ + self.start() + self._connection_made.wait() + if not self.alive: + raise RuntimeError('connection_lost already called') + return self.protocol + + def __exit__(self, exc_type, exc_val, exc_tb): + """Leave context: close port""" + self.close() + + +# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +# test +if __name__ == '__main__': + # pylint: disable=wrong-import-position + import sys + import time + import traceback + + #~ PORT = 'spy:///dev/ttyUSB0' + PORT = 'loop://' + + class PrintLines(LineReader): + def connection_made(self, transport): + super(PrintLines, self).connection_made(transport) + sys.stdout.write('port opened\n') + self.write_line('hello world') + + def handle_line(self, data): + sys.stdout.write('line received: {!r}\n'.format(data)) + + def connection_lost(self, exc): + if exc: + traceback.print_exc(exc) + sys.stdout.write('port closed\n') + + ser = serial.serial_for_url(PORT, baudrate=115200, timeout=1) + with ReaderThread(ser, PrintLines) as protocol: + protocol.write_line('hello') + time.sleep(2) + + # alternative usage + ser = serial.serial_for_url(PORT, baudrate=115200, timeout=1) + t = ReaderThread(ser, PrintLines) + t.start() + transport, protocol = t.connect() + protocol.write_line('hello') + time.sleep(2) + t.close() diff --git a/backend/venv/lib/python3.6/site-packages/serial/threaded/__pycache__/__init__.cpython-36.pyc b/backend/venv/lib/python3.6/site-packages/serial/threaded/__pycache__/__init__.cpython-36.pyc new file mode 100644 index 0000000..daa3e32 Binary files /dev/null and b/backend/venv/lib/python3.6/site-packages/serial/threaded/__pycache__/__init__.cpython-36.pyc differ diff --git a/backend/venv/lib/python3.6/site-packages/serial/tools/__init__.py b/backend/venv/lib/python3.6/site-packages/serial/tools/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/backend/venv/lib/python3.6/site-packages/serial/tools/__init__.pyc b/backend/venv/lib/python3.6/site-packages/serial/tools/__init__.pyc new file mode 100644 index 0000000..318d7d9 Binary files /dev/null and b/backend/venv/lib/python3.6/site-packages/serial/tools/__init__.pyc differ diff --git a/backend/venv/lib/python3.6/site-packages/serial/tools/__pycache__/__init__.cpython-36.pyc b/backend/venv/lib/python3.6/site-packages/serial/tools/__pycache__/__init__.cpython-36.pyc new file mode 100644 index 0000000..563632c Binary files /dev/null and b/backend/venv/lib/python3.6/site-packages/serial/tools/__pycache__/__init__.cpython-36.pyc differ diff --git a/backend/venv/lib/python3.6/site-packages/serial/tools/__pycache__/hexlify_codec.cpython-36.pyc b/backend/venv/lib/python3.6/site-packages/serial/tools/__pycache__/hexlify_codec.cpython-36.pyc new file mode 100644 index 0000000..a4b9fa1 Binary files /dev/null and b/backend/venv/lib/python3.6/site-packages/serial/tools/__pycache__/hexlify_codec.cpython-36.pyc differ diff --git a/backend/venv/lib/python3.6/site-packages/serial/tools/__pycache__/list_ports.cpython-36.pyc b/backend/venv/lib/python3.6/site-packages/serial/tools/__pycache__/list_ports.cpython-36.pyc new file mode 100644 index 0000000..e253433 Binary files /dev/null and b/backend/venv/lib/python3.6/site-packages/serial/tools/__pycache__/list_ports.cpython-36.pyc differ diff --git a/backend/venv/lib/python3.6/site-packages/serial/tools/__pycache__/list_ports_common.cpython-36.pyc b/backend/venv/lib/python3.6/site-packages/serial/tools/__pycache__/list_ports_common.cpython-36.pyc new file mode 100644 index 0000000..162df21 Binary files /dev/null and b/backend/venv/lib/python3.6/site-packages/serial/tools/__pycache__/list_ports_common.cpython-36.pyc differ diff --git a/backend/venv/lib/python3.6/site-packages/serial/tools/__pycache__/list_ports_linux.cpython-36.pyc b/backend/venv/lib/python3.6/site-packages/serial/tools/__pycache__/list_ports_linux.cpython-36.pyc new file mode 100644 index 0000000..7981fca Binary files /dev/null and b/backend/venv/lib/python3.6/site-packages/serial/tools/__pycache__/list_ports_linux.cpython-36.pyc differ diff --git a/backend/venv/lib/python3.6/site-packages/serial/tools/__pycache__/list_ports_osx.cpython-36.pyc b/backend/venv/lib/python3.6/site-packages/serial/tools/__pycache__/list_ports_osx.cpython-36.pyc new file mode 100644 index 0000000..d95fc87 Binary files /dev/null and b/backend/venv/lib/python3.6/site-packages/serial/tools/__pycache__/list_ports_osx.cpython-36.pyc differ diff --git a/backend/venv/lib/python3.6/site-packages/serial/tools/__pycache__/list_ports_posix.cpython-36.pyc b/backend/venv/lib/python3.6/site-packages/serial/tools/__pycache__/list_ports_posix.cpython-36.pyc new file mode 100644 index 0000000..623c95e Binary files /dev/null and b/backend/venv/lib/python3.6/site-packages/serial/tools/__pycache__/list_ports_posix.cpython-36.pyc differ diff --git a/backend/venv/lib/python3.6/site-packages/serial/tools/__pycache__/list_ports_windows.cpython-36.pyc b/backend/venv/lib/python3.6/site-packages/serial/tools/__pycache__/list_ports_windows.cpython-36.pyc new file mode 100644 index 0000000..9e999bf Binary files /dev/null and b/backend/venv/lib/python3.6/site-packages/serial/tools/__pycache__/list_ports_windows.cpython-36.pyc differ diff --git a/backend/venv/lib/python3.6/site-packages/serial/tools/__pycache__/miniterm.cpython-36.pyc b/backend/venv/lib/python3.6/site-packages/serial/tools/__pycache__/miniterm.cpython-36.pyc new file mode 100644 index 0000000..ab83fae Binary files /dev/null and b/backend/venv/lib/python3.6/site-packages/serial/tools/__pycache__/miniterm.cpython-36.pyc differ diff --git a/backend/venv/lib/python3.6/site-packages/serial/tools/hexlify_codec.py b/backend/venv/lib/python3.6/site-packages/serial/tools/hexlify_codec.py new file mode 100644 index 0000000..1371da2 --- /dev/null +++ b/backend/venv/lib/python3.6/site-packages/serial/tools/hexlify_codec.py @@ -0,0 +1,124 @@ +#! python +# +# This is a codec to create and decode hexdumps with spaces between characters. used by miniterm. +# +# This file is part of pySerial. https://github.com/pyserial/pyserial +# (C) 2015-2016 Chris Liechti +# +# SPDX-License-Identifier: BSD-3-Clause +"""\ +Python 'hex' Codec - 2-digit hex with spaces content transfer encoding. + +Encode and decode may be a bit missleading at first sight... + +The textual representation is a hex dump: e.g. "40 41" +The "encoded" data of this is the binary form, e.g. b"@A" + +Therefore decoding is binary to text and thus converting binary data to hex dump. + +""" + +import codecs +import serial + + +try: + unicode +except (NameError, AttributeError): + unicode = str # for Python 3, pylint: disable=redefined-builtin,invalid-name + + +HEXDIGITS = '0123456789ABCDEF' + + +# Codec APIs + +def hex_encode(data, errors='strict'): + """'40 41 42' -> b'@ab'""" + return (serial.to_bytes([int(h, 16) for h in data.split()]), len(data)) + + +def hex_decode(data, errors='strict'): + """b'@ab' -> '40 41 42'""" + return (unicode(''.join('{:02X} '.format(ord(b)) for b in serial.iterbytes(data))), len(data)) + + +class Codec(codecs.Codec): + def encode(self, data, errors='strict'): + """'40 41 42' -> b'@ab'""" + return serial.to_bytes([int(h, 16) for h in data.split()]) + + def decode(self, data, errors='strict'): + """b'@ab' -> '40 41 42'""" + return unicode(''.join('{:02X} '.format(ord(b)) for b in serial.iterbytes(data))) + + +class IncrementalEncoder(codecs.IncrementalEncoder): + """Incremental hex encoder""" + + def __init__(self, errors='strict'): + self.errors = errors + self.state = 0 + + def reset(self): + self.state = 0 + + def getstate(self): + return self.state + + def setstate(self, state): + self.state = state + + def encode(self, data, final=False): + """\ + Incremental encode, keep track of digits and emit a byte when a pair + of hex digits is found. The space is optional unless the error + handling is defined to be 'strict'. + """ + state = self.state + encoded = [] + for c in data.upper(): + if c in HEXDIGITS: + z = HEXDIGITS.index(c) + if state: + encoded.append(z + (state & 0xf0)) + state = 0 + else: + state = 0x100 + (z << 4) + elif c == ' ': # allow spaces to separate values + if state and self.errors == 'strict': + raise UnicodeError('odd number of hex digits') + state = 0 + else: + if self.errors == 'strict': + raise UnicodeError('non-hex digit found: {!r}'.format(c)) + self.state = state + return serial.to_bytes(encoded) + + +class IncrementalDecoder(codecs.IncrementalDecoder): + """Incremental decoder""" + def decode(self, data, final=False): + return unicode(''.join('{:02X} '.format(ord(b)) for b in serial.iterbytes(data))) + + +class StreamWriter(Codec, codecs.StreamWriter): + """Combination of hexlify codec and StreamWriter""" + + +class StreamReader(Codec, codecs.StreamReader): + """Combination of hexlify codec and StreamReader""" + + +def getregentry(): + """encodings module API""" + return codecs.CodecInfo( + name='hexlify', + encode=hex_encode, + decode=hex_decode, + incrementalencoder=IncrementalEncoder, + incrementaldecoder=IncrementalDecoder, + streamwriter=StreamWriter, + streamreader=StreamReader, + #~ _is_text_encoding=True, + ) diff --git a/backend/venv/lib/python3.6/site-packages/serial/tools/hexlify_codec.pyc b/backend/venv/lib/python3.6/site-packages/serial/tools/hexlify_codec.pyc new file mode 100644 index 0000000..d8cfc11 Binary files /dev/null and b/backend/venv/lib/python3.6/site-packages/serial/tools/hexlify_codec.pyc differ diff --git a/backend/venv/lib/python3.6/site-packages/serial/tools/list_ports.py b/backend/venv/lib/python3.6/site-packages/serial/tools/list_ports.py new file mode 100644 index 0000000..827e81f --- /dev/null +++ b/backend/venv/lib/python3.6/site-packages/serial/tools/list_ports.py @@ -0,0 +1,108 @@ +#!/usr/bin/env python +# +# Serial port enumeration. Console tool and backend selection. +# +# This file is part of pySerial. https://github.com/pyserial/pyserial +# (C) 2011-2015 Chris Liechti +# +# SPDX-License-Identifier: BSD-3-Clause + +"""\ +This module will provide a function called comports that returns an +iterable (generator or list) that will enumerate available com ports. Note that +on some systems non-existent ports may be listed. + +Additionally a grep function is supplied that can be used to search for ports +based on their descriptions or hardware ID. +""" + +import sys +import os +import re + +# chose an implementation, depending on os +#~ if sys.platform == 'cli': +#~ else: +if os.name == 'nt': # sys.platform == 'win32': + from serial.tools.list_ports_windows import comports +elif os.name == 'posix': + from serial.tools.list_ports_posix import comports +#~ elif os.name == 'java': +else: + raise ImportError("Sorry: no implementation for your platform ('{}') available".format(os.name)) + +# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + +def grep(regexp, include_links=False): + """\ + Search for ports using a regular expression. Port name, description and + hardware ID are searched. The function returns an iterable that returns the + same tuples as comport() would do. + """ + r = re.compile(regexp, re.I) + for info in comports(include_links): + port, desc, hwid = info + if r.search(port) or r.search(desc) or r.search(hwid): + yield info + + +# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +def main(): + import argparse + + parser = argparse.ArgumentParser(description='Serial port enumeration') + + parser.add_argument( + 'regexp', + nargs='?', + help='only show ports that match this regex') + + parser.add_argument( + '-v', '--verbose', + action='store_true', + help='show more messages') + + parser.add_argument( + '-q', '--quiet', + action='store_true', + help='suppress all messages') + + parser.add_argument( + '-n', + type=int, + help='only output the N-th entry') + + parser.add_argument( + '-s', '--include-links', + action='store_true', + help='include entries that are symlinks to real devices') + + args = parser.parse_args() + + hits = 0 + # get iteraror w/ or w/o filter + if args.regexp: + if not args.quiet: + sys.stderr.write("Filtered list with regexp: {!r}\n".format(args.regexp)) + iterator = sorted(grep(args.regexp, include_links=args.include_links)) + else: + iterator = sorted(comports(include_links=args.include_links)) + # list them + for n, (port, desc, hwid) in enumerate(iterator, 1): + if args.n is None or args.n == n: + sys.stdout.write("{:20}\n".format(port)) + if args.verbose: + sys.stdout.write(" desc: {}\n".format(desc)) + sys.stdout.write(" hwid: {}\n".format(hwid)) + hits += 1 + if not args.quiet: + if hits: + sys.stderr.write("{} ports found\n".format(hits)) + else: + sys.stderr.write("no ports found\n") + +# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +# test +if __name__ == '__main__': + main() diff --git a/backend/venv/lib/python3.6/site-packages/serial/tools/list_ports.pyc b/backend/venv/lib/python3.6/site-packages/serial/tools/list_ports.pyc new file mode 100644 index 0000000..7532ec7 Binary files /dev/null and b/backend/venv/lib/python3.6/site-packages/serial/tools/list_ports.pyc differ diff --git a/backend/venv/lib/python3.6/site-packages/serial/tools/list_ports_common.py b/backend/venv/lib/python3.6/site-packages/serial/tools/list_ports_common.py new file mode 100644 index 0000000..145e63e --- /dev/null +++ b/backend/venv/lib/python3.6/site-packages/serial/tools/list_ports_common.py @@ -0,0 +1,108 @@ +#!/usr/bin/env python +# +# This is a helper module for the various platform dependent list_port +# implementations. +# +# This file is part of pySerial. https://github.com/pyserial/pyserial +# (C) 2015 Chris Liechti +# +# SPDX-License-Identifier: BSD-3-Clause +import re +import glob +import os + + +def numsplit(text): + """\ + Convert string into a list of texts and numbers in order to support a + natural sorting. + """ + result = [] + for group in re.split(r'(\d+)', text): + if group: + try: + group = int(group) + except ValueError: + pass + result.append(group) + return result + + +class ListPortInfo(object): + """Info collection base class for serial ports""" + + def __init__(self, device=None): + self.device = device + self.name = None + self.description = 'n/a' + self.hwid = 'n/a' + # USB specific data + self.vid = None + self.pid = None + self.serial_number = None + self.location = None + self.manufacturer = None + self.product = None + self.interface = None + # special handling for links + if device is not None and os.path.islink(device): + self.hwid = 'LINK={}'.format(os.path.realpath(device)) + + def usb_description(self): + """return a short string to name the port based on USB info""" + if self.interface is not None: + return '{} - {}'.format(self.product, self.interface) + elif self.product is not None: + return self.product + else: + return self.name + + def usb_info(self): + """return a string with USB related information about device""" + return 'USB VID:PID={:04X}:{:04X}{}{}'.format( + self.vid or 0, + self.pid or 0, + ' SER={}'.format(self.serial_number) if self.serial_number is not None else '', + ' LOCATION={}'.format(self.location) if self.location is not None else '') + + def apply_usb_info(self): + """update description and hwid from USB data""" + self.description = self.usb_description() + self.hwid = self.usb_info() + + def __eq__(self, other): + return self.device == other.device + + def __lt__(self, other): + return numsplit(self.device) < numsplit(other.device) + + def __str__(self): + return '{} - {}'.format(self.device, self.description) + + def __getitem__(self, index): + """Item access: backwards compatible -> (port, desc, hwid)""" + if index == 0: + return self.device + elif index == 1: + return self.description + elif index == 2: + return self.hwid + else: + raise IndexError('{} > 2'.format(index)) + +# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +def list_links(devices): + """\ + search all /dev devices and look for symlinks to known ports already + listed in devices. + """ + links = [] + for device in glob.glob('/dev/*'): + if os.path.islink(device) and os.path.realpath(device) in devices: + links.append(device) + return links + +# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +# test +if __name__ == '__main__': + print(ListPortInfo('dummy')) diff --git a/backend/venv/lib/python3.6/site-packages/serial/tools/list_ports_common.pyc b/backend/venv/lib/python3.6/site-packages/serial/tools/list_ports_common.pyc new file mode 100644 index 0000000..67c57f4 Binary files /dev/null and b/backend/venv/lib/python3.6/site-packages/serial/tools/list_ports_common.pyc differ diff --git a/backend/venv/lib/python3.6/site-packages/serial/tools/list_ports_linux.py b/backend/venv/lib/python3.6/site-packages/serial/tools/list_ports_linux.py new file mode 100644 index 0000000..4be27cd --- /dev/null +++ b/backend/venv/lib/python3.6/site-packages/serial/tools/list_ports_linux.py @@ -0,0 +1,107 @@ +#!/usr/bin/env python +# +# This is a module that gathers a list of serial ports including details on +# GNU/Linux systems. +# +# This file is part of pySerial. https://github.com/pyserial/pyserial +# (C) 2011-2015 Chris Liechti +# +# SPDX-License-Identifier: BSD-3-Clause + +import glob +import os +from serial.tools import list_ports_common + + +class SysFS(list_ports_common.ListPortInfo): + """Wrapper for easy sysfs access and device info""" + + def __init__(self, device): + super(SysFS, self).__init__(device) + # special handling for links + if device is not None and os.path.islink(device): + device = os.path.realpath(device) + is_link = True + else: + is_link = False + self.name = os.path.basename(device) + self.usb_device_path = None + if os.path.exists('/sys/class/tty/{}/device'.format(self.name)): + self.device_path = os.path.realpath('/sys/class/tty/{}/device'.format(self.name)) + self.subsystem = os.path.basename(os.path.realpath(os.path.join(self.device_path, 'subsystem'))) + else: + self.device_path = None + self.subsystem = None + # check device type + if self.subsystem == 'usb-serial': + self.usb_interface_path = os.path.dirname(self.device_path) + elif self.subsystem == 'usb': + self.usb_interface_path = self.device_path + else: + self.usb_interface_path = None + # fill-in info for USB devices + if self.usb_interface_path is not None: + self.usb_device_path = os.path.dirname(self.usb_interface_path) + + try: + num_if = int(self.read_line(self.usb_device_path, 'bNumInterfaces')) + except ValueError: + num_if = 1 + + self.vid = int(self.read_line(self.usb_device_path, 'idVendor'), 16) + self.pid = int(self.read_line(self.usb_device_path, 'idProduct'), 16) + self.serial_number = self.read_line(self.usb_device_path, 'serial') + if num_if > 1: # multi interface devices like FT4232 + self.location = os.path.basename(self.usb_interface_path) + else: + self.location = os.path.basename(self.usb_device_path) + + self.manufacturer = self.read_line(self.usb_device_path, 'manufacturer') + self.product = self.read_line(self.usb_device_path, 'product') + self.interface = self.read_line(self.device_path, 'interface') + + if self.subsystem in ('usb', 'usb-serial'): + self.apply_usb_info() + #~ elif self.subsystem in ('pnp', 'amba'): # PCI based devices, raspi + elif self.subsystem == 'pnp': # PCI based devices + self.description = self.name + self.hwid = self.read_line(self.device_path, 'id') + elif self.subsystem == 'amba': # raspi + self.description = self.name + self.hwid = os.path.basename(self.device_path) + + if is_link: + self.hwid += ' LINK={}'.format(device) + + def read_line(self, *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: + with open(os.path.join(*args)) as f: + line = f.readline().strip() + return line + except IOError: + return None + + +def comports(include_links=False): + devices = glob.glob('/dev/ttyS*') # built-in serial ports + devices.extend(glob.glob('/dev/ttyUSB*')) # usb-serial with own driver + devices.extend(glob.glob('/dev/ttyACM*')) # usb-serial with CDC-ACM profile + devices.extend(glob.glob('/dev/ttyAMA*')) # ARM internal port (raspi) + devices.extend(glob.glob('/dev/rfcomm*')) # BT serial devices + devices.extend(glob.glob('/dev/ttyAP*')) # Advantech multi-port serial controllers + if include_links: + devices.extend(list_ports_common.list_links(devices)) + return [info + for info in [SysFS(d) for d in devices] + if info.subsystem != "platform"] # hide non-present internal serial ports + +# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +# test +if __name__ == '__main__': + for port, desc, hwid in sorted(comports()): + print("{}: {} [{}]".format(port, desc, hwid)) diff --git a/backend/venv/lib/python3.6/site-packages/serial/tools/list_ports_linux.pyc b/backend/venv/lib/python3.6/site-packages/serial/tools/list_ports_linux.pyc new file mode 100644 index 0000000..08ffc25 Binary files /dev/null and b/backend/venv/lib/python3.6/site-packages/serial/tools/list_ports_linux.pyc differ diff --git a/backend/venv/lib/python3.6/site-packages/serial/tools/list_ports_osx.py b/backend/venv/lib/python3.6/site-packages/serial/tools/list_ports_osx.py new file mode 100644 index 0000000..79ce4f1 --- /dev/null +++ b/backend/venv/lib/python3.6/site-packages/serial/tools/list_ports_osx.py @@ -0,0 +1,260 @@ +#!/usr/bin/env python +# +# This is a module that gathers a list of serial ports including details on OSX +# +# code originally from https://github.com/makerbot/pyserial/tree/master/serial/tools +# with contributions from cibomahto, dgs3, FarMcKon, tedbrandston +# and modifications by cliechti, hoihu, hardkrash +# +# This file is part of pySerial. https://github.com/pyserial/pyserial +# (C) 2013-2015 +# +# SPDX-License-Identifier: BSD-3-Clause + + +# List all of the callout devices in OS/X by querying IOKit. + +# See the following for a reference of how to do this: +# http://developer.apple.com/library/mac/#documentation/DeviceDrivers/Conceptual/WorkingWSerial/WWSerial_SerialDevs/SerialDevices.html#//apple_ref/doc/uid/TP30000384-CIHGEAFD + +# More help from darwin_hid.py + +# Also see the 'IORegistryExplorer' for an idea of what we are actually searching + +import ctypes +import ctypes.util + +from serial.tools import list_ports_common + +iokit = ctypes.cdll.LoadLibrary(ctypes.util.find_library('IOKit')) +cf = ctypes.cdll.LoadLibrary(ctypes.util.find_library('CoreFoundation')) + +kIOMasterPortDefault = ctypes.c_void_p.in_dll(iokit, "kIOMasterPortDefault") +kCFAllocatorDefault = ctypes.c_void_p.in_dll(cf, "kCFAllocatorDefault") + +kCFStringEncodingMacRoman = 0 + +iokit.IOServiceMatching.restype = ctypes.c_void_p + +iokit.IOServiceGetMatchingServices.argtypes = [ctypes.c_void_p, ctypes.c_void_p, ctypes.c_void_p] +iokit.IOServiceGetMatchingServices.restype = ctypes.c_void_p + +iokit.IORegistryEntryGetParentEntry.argtypes = [ctypes.c_void_p, ctypes.c_void_p, ctypes.c_void_p] + +iokit.IORegistryEntryCreateCFProperty.argtypes = [ctypes.c_void_p, ctypes.c_void_p, ctypes.c_void_p, ctypes.c_uint32] +iokit.IORegistryEntryCreateCFProperty.restype = ctypes.c_void_p + +iokit.IORegistryEntryGetPath.argtypes = [ctypes.c_void_p, ctypes.c_void_p, ctypes.c_void_p] +iokit.IORegistryEntryGetPath.restype = ctypes.c_void_p + +iokit.IORegistryEntryGetName.argtypes = [ctypes.c_void_p, ctypes.c_void_p] +iokit.IORegistryEntryGetName.restype = ctypes.c_void_p + +iokit.IOObjectGetClass.argtypes = [ctypes.c_void_p, ctypes.c_void_p] +iokit.IOObjectGetClass.restype = ctypes.c_void_p + +iokit.IOObjectRelease.argtypes = [ctypes.c_void_p] + + +cf.CFStringCreateWithCString.argtypes = [ctypes.c_void_p, ctypes.c_char_p, ctypes.c_int32] +cf.CFStringCreateWithCString.restype = ctypes.c_void_p + +cf.CFStringGetCStringPtr.argtypes = [ctypes.c_void_p, ctypes.c_uint32] +cf.CFStringGetCStringPtr.restype = ctypes.c_char_p + +cf.CFNumberGetValue.argtypes = [ctypes.c_void_p, ctypes.c_uint32, ctypes.c_void_p] +cf.CFNumberGetValue.restype = ctypes.c_void_p + +# void CFRelease ( CFTypeRef cf ); +cf.CFRelease.argtypes = [ctypes.c_void_p] +cf.CFRelease.restype = None + +# CFNumber type defines +kCFNumberSInt8Type = 1 +kCFNumberSInt16Type = 2 +kCFNumberSInt32Type = 3 +kCFNumberSInt64Type = 4 + + +def get_string_property(device_type, property): + """ + Search the given device for the specified string property + + @param device_type Type of Device + @param property String to search for + @return Python string containing the value, or None if not found. + """ + key = cf.CFStringCreateWithCString( + kCFAllocatorDefault, + property.encode("mac_roman"), + kCFStringEncodingMacRoman) + + CFContainer = iokit.IORegistryEntryCreateCFProperty( + device_type, + key, + kCFAllocatorDefault, + 0) + output = None + + if CFContainer: + output = cf.CFStringGetCStringPtr(CFContainer, 0) + if output is not None: + output = output.decode('mac_roman') + cf.CFRelease(CFContainer) + return output + + +def get_int_property(device_type, property, cf_number_type): + """ + Search the given device for the specified string property + + @param device_type Device to search + @param property String to search for + @param cf_number_type CFType number + + @return Python string containing the value, or None if not found. + """ + key = cf.CFStringCreateWithCString( + kCFAllocatorDefault, + property.encode("mac_roman"), + kCFStringEncodingMacRoman) + + CFContainer = iokit.IORegistryEntryCreateCFProperty( + device_type, + key, + kCFAllocatorDefault, + 0) + + if CFContainer: + if (cf_number_type == kCFNumberSInt32Type): + number = ctypes.c_uint32() + elif (cf_number_type == kCFNumberSInt16Type): + number = ctypes.c_uint16() + cf.CFNumberGetValue(CFContainer, cf_number_type, ctypes.byref(number)) + cf.CFRelease(CFContainer) + return number.value + return None + + +def IORegistryEntryGetName(device): + pathname = ctypes.create_string_buffer(100) # TODO: Is this ok? + iokit.IOObjectGetClass(device, ctypes.byref(pathname)) + return pathname.value + + +def GetParentDeviceByType(device, parent_type): + """ Find the first parent of a device that implements the parent_type + @param IOService Service to inspect + @return Pointer to the parent type, or None if it was not found. + """ + # First, try to walk up the IOService tree to find a parent of this device that is a IOUSBDevice. + parent_type = parent_type.encode('mac_roman') + while IORegistryEntryGetName(device) != parent_type: + parent = ctypes.c_void_p() + response = iokit.IORegistryEntryGetParentEntry( + device, + "IOService".encode("mac_roman"), + ctypes.byref(parent)) + # If we weren't able to find a parent for the device, we're done. + if response != 0: + return None + device = parent + return device + + +def GetIOServicesByType(service_type): + """ + returns iterator over specified service_type + """ + serial_port_iterator = ctypes.c_void_p() + + iokit.IOServiceGetMatchingServices( + kIOMasterPortDefault, + iokit.IOServiceMatching(service_type.encode('mac_roman')), + ctypes.byref(serial_port_iterator)) + + services = [] + while iokit.IOIteratorIsValid(serial_port_iterator): + service = iokit.IOIteratorNext(serial_port_iterator) + if not service: + break + services.append(service) + iokit.IOObjectRelease(serial_port_iterator) + return services + + +def location_to_string(locationID): + """ + helper to calculate port and bus number from locationID + """ + loc = ['{}-'.format(locationID >> 24)] + while locationID & 0xf00000: + if len(loc) > 1: + loc.append('.') + loc.append('{}'.format((locationID >> 20) & 0xf)) + locationID <<= 4 + return ''.join(loc) + + +class SuitableSerialInterface(object): + pass + + +def scan_interfaces(): + """ + helper function to scan USB interfaces + returns a list of SuitableSerialInterface objects with name and id attributes + """ + interfaces = [] + for service in GetIOServicesByType('IOSerialBSDClient'): + device = get_string_property(service, "IOCalloutDevice") + if device: + usb_device = GetParentDeviceByType(service, "IOUSBInterface") + if usb_device: + name = get_string_property(usb_device, "USB Interface Name") or None + locationID = get_int_property(usb_device, "locationID", kCFNumberSInt32Type) or '' + i = SuitableSerialInterface() + i.id = locationID + i.name = name + interfaces.append(i) + return interfaces + + +def search_for_locationID_in_interfaces(serial_interfaces, locationID): + for interface in serial_interfaces: + if (interface.id == locationID): + return interface.name + return None + + +def comports(include_links=False): + # XXX include_links is currently ignored. are links in /dev even supported here? + # Scan for all iokit serial ports + services = GetIOServicesByType('IOSerialBSDClient') + ports = [] + serial_interfaces = scan_interfaces() + for service in services: + # First, add the callout device file. + device = get_string_property(service, "IOCalloutDevice") + if device: + info = list_ports_common.ListPortInfo(device) + # If the serial port is implemented by IOUSBDevice + usb_device = GetParentDeviceByType(service, "IOUSBDevice") + if usb_device: + # fetch some useful informations from properties + info.vid = get_int_property(usb_device, "idVendor", kCFNumberSInt16Type) + info.pid = get_int_property(usb_device, "idProduct", kCFNumberSInt16Type) + info.serial_number = get_string_property(usb_device, "USB Serial Number") + info.product = get_string_property(usb_device, "USB Product Name") or 'n/a' + info.manufacturer = get_string_property(usb_device, "USB Vendor Name") + locationID = get_int_property(usb_device, "locationID", kCFNumberSInt32Type) + info.location = location_to_string(locationID) + info.interface = search_for_locationID_in_interfaces(serial_interfaces, locationID) + info.apply_usb_info() + ports.append(info) + return ports + +# test +if __name__ == '__main__': + for port, desc, hwid in sorted(comports()): + print("{}: {} [{}]".format(port, desc, hwid)) diff --git a/backend/venv/lib/python3.6/site-packages/serial/tools/list_ports_osx.pyc b/backend/venv/lib/python3.6/site-packages/serial/tools/list_ports_osx.pyc new file mode 100644 index 0000000..a5c2c2b Binary files /dev/null and b/backend/venv/lib/python3.6/site-packages/serial/tools/list_ports_osx.pyc differ diff --git a/backend/venv/lib/python3.6/site-packages/serial/tools/list_ports_posix.py b/backend/venv/lib/python3.6/site-packages/serial/tools/list_ports_posix.py new file mode 100644 index 0000000..0d580b0 --- /dev/null +++ b/backend/venv/lib/python3.6/site-packages/serial/tools/list_ports_posix.py @@ -0,0 +1,117 @@ +#!/usr/bin/env python +# +# This is a module that gathers a list of serial ports on POSIXy systems. +# For some specific implementations, see also list_ports_linux, list_ports_osx +# +# This file is part of pySerial. https://github.com/pyserial/pyserial +# (C) 2011-2015 Chris Liechti +# +# SPDX-License-Identifier: BSD-3-Clause + +"""\ +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. +""" + +import glob +import sys +import os +from serial.tools import list_ports_common + +# try to detect the OS so that a device can be selected... +plat = sys.platform.lower() + +if plat[:5] == 'linux': # Linux (confirmed) # noqa + from serial.tools.list_ports_linux import comports + +elif plat[:6] == 'darwin': # OS X (confirmed) + from serial.tools.list_ports_osx import comports + +elif plat == 'cygwin': # cygwin/win32 + # cygwin accepts /dev/com* in many contexts + # (such as 'open' call, explicit 'ls'), but 'glob.glob' + # and bare 'ls' do not; so use /dev/ttyS* instead + def comports(include_links=False): + devices = glob.glob('/dev/ttyS*') + if include_links: + devices.extend(list_ports_common.list_links(devices)) + return [list_ports_common.ListPortInfo(d) for d in devices] + +elif plat[:7] == 'openbsd': # OpenBSD + def comports(include_links=False): + devices = glob.glob('/dev/cua*') + if include_links: + devices.extend(list_ports_common.list_links(devices)) + return [list_ports_common.ListPortInfo(d) for d in devices] + +elif plat[:3] == 'bsd' or plat[:7] == 'freebsd': + def comports(include_links=False): + devices = glob.glob('/dev/cua*[!.init][!.lock]') + if include_links: + devices.extend(list_ports_common.list_links(devices)) + return [list_ports_common.ListPortInfo(d) for d in devices] + +elif plat[:6] == 'netbsd': # NetBSD + def comports(include_links=False): + """scan for available ports. return a list of device names.""" + devices = glob.glob('/dev/dty*') + if include_links: + devices.extend(list_ports_common.list_links(devices)) + return [list_ports_common.ListPortInfo(d) for d in devices] + +elif plat[:4] == 'irix': # IRIX + def comports(include_links=False): + """scan for available ports. return a list of device names.""" + devices = glob.glob('/dev/ttyf*') + if include_links: + devices.extend(list_ports_common.list_links(devices)) + return [list_ports_common.ListPortInfo(d) for d in devices] + +elif plat[:2] == 'hp': # HP-UX (not tested) + def comports(include_links=False): + """scan for available ports. return a list of device names.""" + devices = glob.glob('/dev/tty*p0') + if include_links: + devices.extend(list_ports_common.list_links(devices)) + return [list_ports_common.ListPortInfo(d) for d in devices] + +elif plat[:5] == 'sunos': # Solaris/SunOS + def comports(include_links=False): + """scan for available ports. return a list of device names.""" + devices = glob.glob('/dev/tty*c') + if include_links: + devices.extend(list_ports_common.list_links(devices)) + return [list_ports_common.ListPortInfo(d) for d in devices] + +elif plat[:3] == 'aix': # AIX + def comports(include_links=False): + """scan for available ports. return a list of device names.""" + devices = glob.glob('/dev/tty*') + if include_links: + devices.extend(list_ports_common.list_links(devices)) + return [list_ports_common.ListPortInfo(d) for d in devices] + +else: + # platform detection has failed... + import serial + sys.stderr.write("""\ +don't know how to enumerate ttys on this system. +! I you know how the serial ports are named send this information to +! the author of this module: + +sys.platform = {!r} +os.name = {!r} +pySerial version = {} + +also add the naming scheme of the serial ports and with a bit luck you can get +this module running... +""".format(sys.platform, os.name, serial.VERSION)) + raise ImportError("Sorry: no implementation for your platform ('{}') available".format(os.name)) + +# test +if __name__ == '__main__': + for port, desc, hwid in sorted(comports()): + print("{}: {} [{}]".format(port, desc, hwid)) diff --git a/backend/venv/lib/python3.6/site-packages/serial/tools/list_ports_posix.pyc b/backend/venv/lib/python3.6/site-packages/serial/tools/list_ports_posix.pyc new file mode 100644 index 0000000..184c3f3 Binary files /dev/null and b/backend/venv/lib/python3.6/site-packages/serial/tools/list_ports_posix.pyc differ diff --git a/backend/venv/lib/python3.6/site-packages/serial/tools/list_ports_windows.py b/backend/venv/lib/python3.6/site-packages/serial/tools/list_ports_windows.py new file mode 100644 index 0000000..f28047b --- /dev/null +++ b/backend/venv/lib/python3.6/site-packages/serial/tools/list_ports_windows.py @@ -0,0 +1,305 @@ +#! python +# +# Enumerate serial ports on Windows including a human readable description +# and hardware information. +# +# This file is part of pySerial. https://github.com/pyserial/pyserial +# (C) 2001-2016 Chris Liechti +# +# SPDX-License-Identifier: BSD-3-Clause + +# pylint: disable=invalid-name,too-few-public-methods +import re +import ctypes +from ctypes.wintypes import BOOL +from ctypes.wintypes import HWND +from ctypes.wintypes import DWORD +from ctypes.wintypes import WORD +from ctypes.wintypes import LONG +from ctypes.wintypes import ULONG +from ctypes.wintypes import HKEY +from ctypes.wintypes import BYTE +import serial +from serial.win32 import ULONG_PTR +from serial.tools import list_ports_common + + +def ValidHandle(value, func, arguments): + if value == 0: + raise ctypes.WinError() + return value + + +NULL = 0 +HDEVINFO = ctypes.c_void_p +LPCTSTR = ctypes.c_wchar_p +PCTSTR = ctypes.c_wchar_p +PTSTR = ctypes.c_wchar_p +LPDWORD = PDWORD = ctypes.POINTER(DWORD) +#~ LPBYTE = PBYTE = ctypes.POINTER(BYTE) +LPBYTE = PBYTE = ctypes.c_void_p # XXX avoids error about types + +ACCESS_MASK = DWORD +REGSAM = ACCESS_MASK + + +class GUID(ctypes.Structure): + _fields_ = [ + ('Data1', DWORD), + ('Data2', WORD), + ('Data3', WORD), + ('Data4', BYTE * 8), + ] + + def __str__(self): + return "{{{:08x}-{:04x}-{:04x}-{}-{}}}".format( + self.Data1, + self.Data2, + self.Data3, + ''.join(["{:02x}".format(d) for d in self.Data4[:2]]), + ''.join(["{:02x}".format(d) for d in self.Data4[2:]]), + ) + + +class SP_DEVINFO_DATA(ctypes.Structure): + _fields_ = [ + ('cbSize', DWORD), + ('ClassGuid', GUID), + ('DevInst', DWORD), + ('Reserved', ULONG_PTR), + ] + + def __str__(self): + return "ClassGuid:{} DevInst:{}".format(self.ClassGuid, self.DevInst) + + +PSP_DEVINFO_DATA = ctypes.POINTER(SP_DEVINFO_DATA) + +PSP_DEVICE_INTERFACE_DETAIL_DATA = ctypes.c_void_p + +setupapi = ctypes.windll.LoadLibrary("setupapi") +SetupDiDestroyDeviceInfoList = setupapi.SetupDiDestroyDeviceInfoList +SetupDiDestroyDeviceInfoList.argtypes = [HDEVINFO] +SetupDiDestroyDeviceInfoList.restype = BOOL + +SetupDiClassGuidsFromName = setupapi.SetupDiClassGuidsFromNameW +SetupDiClassGuidsFromName.argtypes = [PCTSTR, ctypes.POINTER(GUID), DWORD, PDWORD] +SetupDiClassGuidsFromName.restype = BOOL + +SetupDiEnumDeviceInfo = setupapi.SetupDiEnumDeviceInfo +SetupDiEnumDeviceInfo.argtypes = [HDEVINFO, DWORD, PSP_DEVINFO_DATA] +SetupDiEnumDeviceInfo.restype = BOOL + +SetupDiGetClassDevs = setupapi.SetupDiGetClassDevsW +SetupDiGetClassDevs.argtypes = [ctypes.POINTER(GUID), PCTSTR, HWND, DWORD] +SetupDiGetClassDevs.restype = HDEVINFO +SetupDiGetClassDevs.errcheck = ValidHandle + +SetupDiGetDeviceRegistryProperty = setupapi.SetupDiGetDeviceRegistryPropertyW +SetupDiGetDeviceRegistryProperty.argtypes = [HDEVINFO, PSP_DEVINFO_DATA, DWORD, PDWORD, PBYTE, DWORD, PDWORD] +SetupDiGetDeviceRegistryProperty.restype = BOOL + +SetupDiGetDeviceInstanceId = setupapi.SetupDiGetDeviceInstanceIdW +SetupDiGetDeviceInstanceId.argtypes = [HDEVINFO, PSP_DEVINFO_DATA, PTSTR, DWORD, PDWORD] +SetupDiGetDeviceInstanceId.restype = BOOL + +SetupDiOpenDevRegKey = setupapi.SetupDiOpenDevRegKey +SetupDiOpenDevRegKey.argtypes = [HDEVINFO, PSP_DEVINFO_DATA, DWORD, DWORD, DWORD, REGSAM] +SetupDiOpenDevRegKey.restype = HKEY + +advapi32 = ctypes.windll.LoadLibrary("Advapi32") +RegCloseKey = advapi32.RegCloseKey +RegCloseKey.argtypes = [HKEY] +RegCloseKey.restype = LONG + +RegQueryValueEx = advapi32.RegQueryValueExW +RegQueryValueEx.argtypes = [HKEY, LPCTSTR , LPDWORD, LPDWORD, LPBYTE, LPDWORD] +RegQueryValueEx.restype = LONG + + +DIGCF_PRESENT = 2 +DIGCF_DEVICEINTERFACE = 16 +INVALID_HANDLE_VALUE = 0 +ERROR_INSUFFICIENT_BUFFER = 122 +SPDRP_HARDWAREID = 1 +SPDRP_FRIENDLYNAME = 12 +SPDRP_LOCATION_PATHS = 35 +SPDRP_MFG = 11 +DICS_FLAG_GLOBAL = 1 +DIREG_DEV = 0x00000001 +KEY_READ = 0x20019 + + +def iterate_comports(): + """Return a generator that yields descriptions for serial ports""" + GUIDs = (GUID * 8)() # so far only seen one used, so hope 8 are enough... + guids_size = DWORD() + if not SetupDiClassGuidsFromName( + "Ports", + GUIDs, + ctypes.sizeof(GUIDs), + ctypes.byref(guids_size)): + raise ctypes.WinError() + + # repeat for all possible GUIDs + for index in range(guids_size.value): + bInterfaceNumber = None + g_hdi = SetupDiGetClassDevs( + ctypes.byref(GUIDs[index]), + None, + NULL, + DIGCF_PRESENT) # was DIGCF_PRESENT|DIGCF_DEVICEINTERFACE which misses CDC ports + + devinfo = SP_DEVINFO_DATA() + devinfo.cbSize = ctypes.sizeof(devinfo) + index = 0 + while SetupDiEnumDeviceInfo(g_hdi, index, ctypes.byref(devinfo)): + index += 1 + + # get the real com port name + hkey = SetupDiOpenDevRegKey( + g_hdi, + ctypes.byref(devinfo), + DICS_FLAG_GLOBAL, + 0, + DIREG_DEV, # DIREG_DRV for SW info + KEY_READ) + port_name_buffer = ctypes.create_unicode_buffer(250) + port_name_length = ULONG(ctypes.sizeof(port_name_buffer)) + RegQueryValueEx( + hkey, + "PortName", + None, + None, + ctypes.byref(port_name_buffer), + ctypes.byref(port_name_length)) + RegCloseKey(hkey) + + # unfortunately does this method also include parallel ports. + # we could check for names starting with COM or just exclude LPT + # and hope that other "unknown" names are serial ports... + if port_name_buffer.value.startswith('LPT'): + continue + + # hardware ID + szHardwareID = ctypes.create_unicode_buffer(250) + # try to get ID that includes serial number + if not SetupDiGetDeviceInstanceId( + g_hdi, + ctypes.byref(devinfo), + #~ ctypes.byref(szHardwareID), + szHardwareID, + ctypes.sizeof(szHardwareID) - 1, + None): + # fall back to more generic hardware ID if that would fail + if not SetupDiGetDeviceRegistryProperty( + g_hdi, + ctypes.byref(devinfo), + SPDRP_HARDWAREID, + None, + ctypes.byref(szHardwareID), + ctypes.sizeof(szHardwareID) - 1, + None): + # Ignore ERROR_INSUFFICIENT_BUFFER + if ctypes.GetLastError() != ERROR_INSUFFICIENT_BUFFER: + raise ctypes.WinError() + # stringify + szHardwareID_str = szHardwareID.value + + info = list_ports_common.ListPortInfo(port_name_buffer.value) + + # in case of USB, make a more readable string, similar to that form + # that we also generate on other platforms + if szHardwareID_str.startswith('USB'): + m = re.search(r'VID_([0-9a-f]{4})(&PID_([0-9a-f]{4}))?(&MI_(\d{2}))?(\\(\w+))?', szHardwareID_str, re.I) + if m: + info.vid = int(m.group(1), 16) + if m.group(3): + info.pid = int(m.group(3), 16) + if m.group(5): + bInterfaceNumber = int(m.group(5)) + if m.group(7): + info.serial_number = m.group(7) + # calculate a location string + loc_path_str = ctypes.create_unicode_buffer(250) + if SetupDiGetDeviceRegistryProperty( + g_hdi, + ctypes.byref(devinfo), + SPDRP_LOCATION_PATHS, + None, + ctypes.byref(loc_path_str), + ctypes.sizeof(loc_path_str) - 1, + None): + m = re.finditer(r'USBROOT\((\w+)\)|#USB\((\w+)\)', loc_path_str.value) + location = [] + for g in m: + if g.group(1): + location.append('{:d}'.format(int(g.group(1)) + 1)) + else: + if len(location) > 1: + location.append('.') + else: + location.append('-') + location.append(g.group(2)) + if bInterfaceNumber is not None: + location.append(':{}.{}'.format( + 'x', # XXX how to determine correct bConfigurationValue? + bInterfaceNumber)) + if location: + info.location = ''.join(location) + info.hwid = info.usb_info() + elif szHardwareID_str.startswith('FTDIBUS'): + m = re.search(r'VID_([0-9a-f]{4})\+PID_([0-9a-f]{4})(\+(\w+))?', szHardwareID_str, re.I) + if m: + info.vid = int(m.group(1), 16) + info.pid = int(m.group(2), 16) + if m.group(4): + info.serial_number = m.group(4) + # USB location is hidden by FDTI driver :( + info.hwid = info.usb_info() + else: + info.hwid = szHardwareID_str + + # friendly name + szFriendlyName = ctypes.create_unicode_buffer(250) + if SetupDiGetDeviceRegistryProperty( + g_hdi, + ctypes.byref(devinfo), + SPDRP_FRIENDLYNAME, + #~ SPDRP_DEVICEDESC, + None, + ctypes.byref(szFriendlyName), + ctypes.sizeof(szFriendlyName) - 1, + None): + info.description = szFriendlyName.value + #~ else: + # Ignore ERROR_INSUFFICIENT_BUFFER + #~ if ctypes.GetLastError() != ERROR_INSUFFICIENT_BUFFER: + #~ raise IOError("failed to get details for %s (%s)" % (devinfo, szHardwareID.value)) + # ignore errors and still include the port in the list, friendly name will be same as port name + + # manufacturer + szManufacturer = ctypes.create_unicode_buffer(250) + if SetupDiGetDeviceRegistryProperty( + g_hdi, + ctypes.byref(devinfo), + SPDRP_MFG, + #~ SPDRP_DEVICEDESC, + None, + ctypes.byref(szManufacturer), + ctypes.sizeof(szManufacturer) - 1, + None): + info.manufacturer = szManufacturer.value + yield info + SetupDiDestroyDeviceInfoList(g_hdi) + + +def comports(include_links=False): + """Return a list of info objects about serial ports""" + return list(iterate_comports()) + +# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +# test +if __name__ == '__main__': + for port, desc, hwid in sorted(comports()): + print("{}: {} [{}]".format(port, desc, hwid)) diff --git a/backend/venv/lib/python3.6/site-packages/serial/tools/list_ports_windows.pyc b/backend/venv/lib/python3.6/site-packages/serial/tools/list_ports_windows.pyc new file mode 100644 index 0000000..fa5f4b6 Binary files /dev/null and b/backend/venv/lib/python3.6/site-packages/serial/tools/list_ports_windows.pyc differ diff --git a/backend/venv/lib/python3.6/site-packages/serial/tools/miniterm.py b/backend/venv/lib/python3.6/site-packages/serial/tools/miniterm.py new file mode 100644 index 0000000..88307c6 --- /dev/null +++ b/backend/venv/lib/python3.6/site-packages/serial/tools/miniterm.py @@ -0,0 +1,976 @@ +#!/usr/bin/env python +# +# Very simple serial terminal +# +# This file is part of pySerial. https://github.com/pyserial/pyserial +# (C)2002-2015 Chris Liechti +# +# SPDX-License-Identifier: BSD-3-Clause + +import codecs +import os +import sys +import threading + +import serial +from serial.tools.list_ports import comports +from serial.tools import hexlify_codec + +# pylint: disable=wrong-import-order,wrong-import-position + +codecs.register(lambda c: hexlify_codec.getregentry() if c == 'hexlify' else None) + +try: + raw_input +except NameError: + # pylint: disable=redefined-builtin,invalid-name + raw_input = input # in python3 it's "raw" + unichr = chr + + +def key_description(character): + """generate a readable description for a key""" + ascii_code = ord(character) + if ascii_code < 32: + return 'Ctrl+{:c}'.format(ord('@') + ascii_code) + else: + return repr(character) + + +# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +class ConsoleBase(object): + """OS abstraction for console (input/output codec, no echo)""" + + def __init__(self): + if sys.version_info >= (3, 0): + self.byte_output = sys.stdout.buffer + else: + self.byte_output = sys.stdout + self.output = sys.stdout + + def setup(self): + """Set console to read single characters, no echo""" + + def cleanup(self): + """Restore default console settings""" + + def getkey(self): + """Read a single key from the console""" + return None + + def write_bytes(self, byte_string): + """Write bytes (already encoded)""" + self.byte_output.write(byte_string) + self.byte_output.flush() + + def write(self, text): + """Write string""" + self.output.write(text) + self.output.flush() + + def cancel(self): + """Cancel getkey operation""" + + # - - - - - - - - - - - - - - - - - - - - - - - - + # context manager: + # switch terminal temporary to normal mode (e.g. to get user input) + + def __enter__(self): + self.cleanup() + return self + + def __exit__(self, *args, **kwargs): + self.setup() + + +if os.name == 'nt': # noqa + import msvcrt + import ctypes + + class Out(object): + """file-like wrapper that uses os.write""" + + def __init__(self, fd): + self.fd = fd + + def flush(self): + pass + + def write(self, s): + os.write(self.fd, s) + + class Console(ConsoleBase): + def __init__(self): + super(Console, self).__init__() + self._saved_ocp = ctypes.windll.kernel32.GetConsoleOutputCP() + self._saved_icp = ctypes.windll.kernel32.GetConsoleCP() + ctypes.windll.kernel32.SetConsoleOutputCP(65001) + ctypes.windll.kernel32.SetConsoleCP(65001) + self.output = codecs.getwriter('UTF-8')(Out(sys.stdout.fileno()), 'replace') + # the change of the code page is not propagated to Python, manually fix it + sys.stderr = codecs.getwriter('UTF-8')(Out(sys.stderr.fileno()), 'replace') + sys.stdout = self.output + self.output.encoding = 'UTF-8' # needed for input + + def __del__(self): + ctypes.windll.kernel32.SetConsoleOutputCP(self._saved_ocp) + ctypes.windll.kernel32.SetConsoleCP(self._saved_icp) + + def getkey(self): + while True: + z = msvcrt.getwch() + if z == unichr(13): + return unichr(10) + elif z in (unichr(0), unichr(0x0e)): # functions keys, ignore + msvcrt.getwch() + else: + return z + + def cancel(self): + # CancelIo, CancelSynchronousIo do not seem to work when using + # getwch, so instead, send a key to the window with the console + hwnd = ctypes.windll.kernel32.GetConsoleWindow() + ctypes.windll.user32.PostMessageA(hwnd, 0x100, 0x0d, 0) + +elif os.name == 'posix': + import atexit + import termios + import fcntl + + class Console(ConsoleBase): + def __init__(self): + super(Console, self).__init__() + self.fd = sys.stdin.fileno() + self.old = termios.tcgetattr(self.fd) + atexit.register(self.cleanup) + if sys.version_info < (3, 0): + self.enc_stdin = codecs.getreader(sys.stdin.encoding)(sys.stdin) + else: + self.enc_stdin = sys.stdin + + def setup(self): + new = termios.tcgetattr(self.fd) + new[3] = new[3] & ~termios.ICANON & ~termios.ECHO & ~termios.ISIG + new[6][termios.VMIN] = 1 + new[6][termios.VTIME] = 0 + termios.tcsetattr(self.fd, termios.TCSANOW, new) + + def getkey(self): + c = self.enc_stdin.read(1) + if c == unichr(0x7f): + c = unichr(8) # map the BS key (which yields DEL) to backspace + return c + + def cancel(self): + fcntl.ioctl(self.fd, termios.TIOCSTI, b'\0') + + def cleanup(self): + termios.tcsetattr(self.fd, termios.TCSAFLUSH, self.old) + +else: + raise NotImplementedError( + 'Sorry no implementation for your platform ({}) available.'.format(sys.platform)) + + +# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + +class Transform(object): + """do-nothing: forward all data unchanged""" + def rx(self, text): + """text received from serial port""" + return text + + def tx(self, text): + """text to be sent to serial port""" + return text + + def echo(self, text): + """text to be sent but displayed on console""" + return text + + +class CRLF(Transform): + """ENTER sends CR+LF""" + + def tx(self, text): + return text.replace('\n', '\r\n') + + +class CR(Transform): + """ENTER sends CR""" + + def rx(self, text): + return text.replace('\r', '\n') + + def tx(self, text): + return text.replace('\n', '\r') + + +class LF(Transform): + """ENTER sends LF""" + + +class NoTerminal(Transform): + """remove typical terminal control codes from input""" + + REPLACEMENT_MAP = dict((x, 0x2400 + x) for x in range(32) if unichr(x) not in '\r\n\b\t') + REPLACEMENT_MAP.update( + { + 0x7F: 0x2421, # DEL + 0x9B: 0x2425, # CSI + }) + + def rx(self, text): + return text.translate(self.REPLACEMENT_MAP) + + echo = rx + + +class NoControls(NoTerminal): + """Remove all control codes, incl. CR+LF""" + + REPLACEMENT_MAP = dict((x, 0x2400 + x) for x in range(32)) + REPLACEMENT_MAP.update( + { + 0x20: 0x2423, # visual space + 0x7F: 0x2421, # DEL + 0x9B: 0x2425, # CSI + }) + + +class Printable(Transform): + """Show decimal code for all non-ASCII characters and replace most control codes""" + + def rx(self, text): + r = [] + for c in text: + if ' ' <= c < '\x7f' or c in '\r\n\b\t': + r.append(c) + elif c < ' ': + r.append(unichr(0x2400 + ord(c))) + else: + r.extend(unichr(0x2080 + ord(d) - 48) for d in '{:d}'.format(ord(c))) + r.append(' ') + return ''.join(r) + + echo = rx + + +class Colorize(Transform): + """Apply different colors for received and echo""" + + def __init__(self): + # XXX make it configurable, use colorama? + self.input_color = '\x1b[37m' + self.echo_color = '\x1b[31m' + + def rx(self, text): + return self.input_color + text + + def echo(self, text): + return self.echo_color + text + + +class DebugIO(Transform): + """Print what is sent and received""" + + def rx(self, text): + sys.stderr.write(' [RX:{}] '.format(repr(text))) + sys.stderr.flush() + return text + + def tx(self, text): + sys.stderr.write(' [TX:{}] '.format(repr(text))) + sys.stderr.flush() + return text + + +# other ideas: +# - add date/time for each newline +# - insert newline after: a) timeout b) packet end character + +EOL_TRANSFORMATIONS = { + 'crlf': CRLF, + 'cr': CR, + 'lf': LF, +} + +TRANSFORMATIONS = { + 'direct': Transform, # no transformation + 'default': NoTerminal, + 'nocontrol': NoControls, + 'printable': Printable, + 'colorize': Colorize, + 'debug': DebugIO, +} + + +# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +def ask_for_port(): + """\ + Show a list of ports and ask the user for a choice. To make selection + easier on systems with long device names, also allow the input of an + index. + """ + sys.stderr.write('\n--- Available ports:\n') + ports = [] + for n, (port, desc, hwid) in enumerate(sorted(comports()), 1): + sys.stderr.write('--- {:2}: {:20} {!r}\n'.format(n, port, desc)) + ports.append(port) + while True: + port = raw_input('--- Enter port index or full name: ') + try: + index = int(port) - 1 + if not 0 <= index < len(ports): + sys.stderr.write('--- Invalid index!\n') + continue + except ValueError: + pass + else: + port = ports[index] + return port + + +class Miniterm(object): + """\ + Terminal application. Copy data from serial port to console and vice versa. + Handle special keys from the console to show menu etc. + """ + + def __init__(self, serial_instance, echo=False, eol='crlf', filters=()): + self.console = Console() + self.serial = serial_instance + self.echo = echo + self.raw = False + self.input_encoding = 'UTF-8' + self.output_encoding = 'UTF-8' + self.eol = eol + self.filters = filters + self.update_transformations() + self.exit_character = 0x1d # GS/CTRL+] + self.menu_character = 0x14 # Menu: CTRL+T + self.alive = None + self._reader_alive = None + self.receiver_thread = None + self.rx_decoder = None + self.tx_decoder = None + + def _start_reader(self): + """Start reader thread""" + self._reader_alive = True + # start serial->console thread + self.receiver_thread = threading.Thread(target=self.reader, name='rx') + self.receiver_thread.daemon = True + self.receiver_thread.start() + + def _stop_reader(self): + """Stop reader thread only, wait for clean exit of thread""" + self._reader_alive = False + if hasattr(self.serial, 'cancel_read'): + self.serial.cancel_read() + self.receiver_thread.join() + + def start(self): + """start worker threads""" + self.alive = True + self._start_reader() + # enter console->serial loop + self.transmitter_thread = threading.Thread(target=self.writer, name='tx') + self.transmitter_thread.daemon = True + self.transmitter_thread.start() + self.console.setup() + + def stop(self): + """set flag to stop worker threads""" + self.alive = False + + def join(self, transmit_only=False): + """wait for worker threads to terminate""" + self.transmitter_thread.join() + if not transmit_only: + if hasattr(self.serial, 'cancel_read'): + self.serial.cancel_read() + self.receiver_thread.join() + + def close(self): + self.serial.close() + + def update_transformations(self): + """take list of transformation classes and instantiate them for rx and tx""" + transformations = [EOL_TRANSFORMATIONS[self.eol]] + [TRANSFORMATIONS[f] + for f in self.filters] + self.tx_transformations = [t() for t in transformations] + self.rx_transformations = list(reversed(self.tx_transformations)) + + def set_rx_encoding(self, encoding, errors='replace'): + """set encoding for received data""" + self.input_encoding = encoding + self.rx_decoder = codecs.getincrementaldecoder(encoding)(errors) + + def set_tx_encoding(self, encoding, errors='replace'): + """set encoding for transmitted data""" + self.output_encoding = encoding + self.tx_encoder = codecs.getincrementalencoder(encoding)(errors) + + def dump_port_settings(self): + """Write current settings to sys.stderr""" + sys.stderr.write("\n--- Settings: {p.name} {p.baudrate},{p.bytesize},{p.parity},{p.stopbits}\n".format( + p=self.serial)) + sys.stderr.write('--- RTS: {:8} DTR: {:8} BREAK: {:8}\n'.format( + ('active' if self.serial.rts else 'inactive'), + ('active' if self.serial.dtr else 'inactive'), + ('active' if self.serial.break_condition else 'inactive'))) + try: + sys.stderr.write('--- CTS: {:8} DSR: {:8} RI: {:8} CD: {:8}\n'.format( + ('active' if self.serial.cts else 'inactive'), + ('active' if self.serial.dsr else 'inactive'), + ('active' if self.serial.ri else 'inactive'), + ('active' if self.serial.cd else 'inactive'))) + except serial.SerialException: + # on RFC 2217 ports, it can happen if no modem state notification was + # yet received. ignore this error. + pass + sys.stderr.write('--- software flow control: {}\n'.format('active' if self.serial.xonxoff else 'inactive')) + sys.stderr.write('--- hardware flow control: {}\n'.format('active' if self.serial.rtscts else 'inactive')) + sys.stderr.write('--- serial input encoding: {}\n'.format(self.input_encoding)) + sys.stderr.write('--- serial output encoding: {}\n'.format(self.output_encoding)) + sys.stderr.write('--- EOL: {}\n'.format(self.eol.upper())) + sys.stderr.write('--- filters: {}\n'.format(' '.join(self.filters))) + + def reader(self): + """loop and copy serial->console""" + try: + while self.alive and self._reader_alive: + # read all that is there or wait for one byte + data = self.serial.read(self.serial.in_waiting or 1) + if data: + if self.raw: + self.console.write_bytes(data) + else: + text = self.rx_decoder.decode(data) + for transformation in self.rx_transformations: + text = transformation.rx(text) + self.console.write(text) + except serial.SerialException: + self.alive = False + self.console.cancel() + raise # XXX handle instead of re-raise? + + def writer(self): + """\ + Loop and copy console->serial until self.exit_character character is + found. When self.menu_character is found, interpret the next key + locally. + """ + menu_active = False + try: + while self.alive: + try: + c = self.console.getkey() + except KeyboardInterrupt: + c = '\x03' + if not self.alive: + break + if menu_active: + self.handle_menu_key(c) + menu_active = False + elif c == self.menu_character: + menu_active = True # next char will be for menu + elif c == self.exit_character: + self.stop() # exit app + break + else: + #~ if self.raw: + text = c + for transformation in self.tx_transformations: + text = transformation.tx(text) + self.serial.write(self.tx_encoder.encode(text)) + if self.echo: + echo_text = c + for transformation in self.tx_transformations: + echo_text = transformation.echo(echo_text) + self.console.write(echo_text) + except: + self.alive = False + raise + + def handle_menu_key(self, c): + """Implement a simple menu / settings""" + if c == self.menu_character or c == self.exit_character: + # Menu/exit character again -> send itself + self.serial.write(self.tx_encoder.encode(c)) + if self.echo: + self.console.write(c) + elif c == '\x15': # CTRL+U -> upload file + self.upload_file() + elif c in '\x08hH?': # CTRL+H, h, H, ? -> Show help + sys.stderr.write(self.get_help_text()) + elif c == '\x12': # CTRL+R -> Toggle RTS + self.serial.rts = not self.serial.rts + sys.stderr.write('--- RTS {} ---\n'.format('active' if self.serial.rts else 'inactive')) + elif c == '\x04': # CTRL+D -> Toggle DTR + self.serial.dtr = not self.serial.dtr + sys.stderr.write('--- DTR {} ---\n'.format('active' if self.serial.dtr else 'inactive')) + elif c == '\x02': # CTRL+B -> toggle BREAK condition + self.serial.break_condition = not self.serial.break_condition + sys.stderr.write('--- BREAK {} ---\n'.format('active' if self.serial.break_condition else 'inactive')) + elif c == '\x05': # CTRL+E -> toggle local echo + self.echo = not self.echo + sys.stderr.write('--- local echo {} ---\n'.format('active' if self.echo else 'inactive')) + elif c == '\x06': # CTRL+F -> edit filters + self.change_filter() + elif c == '\x0c': # CTRL+L -> EOL mode + modes = list(EOL_TRANSFORMATIONS) # keys + eol = modes.index(self.eol) + 1 + if eol >= len(modes): + eol = 0 + self.eol = modes[eol] + sys.stderr.write('--- EOL: {} ---\n'.format(self.eol.upper())) + self.update_transformations() + elif c == '\x01': # CTRL+A -> set encoding + self.change_encoding() + elif c == '\x09': # CTRL+I -> info + self.dump_port_settings() + #~ elif c == '\x01': # CTRL+A -> cycle escape mode + #~ elif c == '\x0c': # CTRL+L -> cycle linefeed mode + elif c in 'pP': # P -> change port + self.change_port() + elif c in 'sS': # S -> suspend / open port temporarily + self.suspend_port() + elif c in 'bB': # B -> change baudrate + self.change_baudrate() + elif c == '8': # 8 -> change to 8 bits + self.serial.bytesize = serial.EIGHTBITS + self.dump_port_settings() + elif c == '7': # 7 -> change to 8 bits + self.serial.bytesize = serial.SEVENBITS + self.dump_port_settings() + elif c in 'eE': # E -> change to even parity + self.serial.parity = serial.PARITY_EVEN + self.dump_port_settings() + elif c in 'oO': # O -> change to odd parity + self.serial.parity = serial.PARITY_ODD + self.dump_port_settings() + elif c in 'mM': # M -> change to mark parity + self.serial.parity = serial.PARITY_MARK + self.dump_port_settings() + elif c in 'sS': # S -> change to space parity + self.serial.parity = serial.PARITY_SPACE + self.dump_port_settings() + elif c in 'nN': # N -> change to no parity + self.serial.parity = serial.PARITY_NONE + self.dump_port_settings() + elif c == '1': # 1 -> change to 1 stop bits + self.serial.stopbits = serial.STOPBITS_ONE + self.dump_port_settings() + elif c == '2': # 2 -> change to 2 stop bits + self.serial.stopbits = serial.STOPBITS_TWO + self.dump_port_settings() + elif c == '3': # 3 -> change to 1.5 stop bits + self.serial.stopbits = serial.STOPBITS_ONE_POINT_FIVE + self.dump_port_settings() + elif c in 'xX': # X -> change software flow control + self.serial.xonxoff = (c == 'X') + self.dump_port_settings() + elif c in 'rR': # R -> change hardware flow control + self.serial.rtscts = (c == 'R') + self.dump_port_settings() + else: + sys.stderr.write('--- unknown menu character {} --\n'.format(key_description(c))) + + def upload_file(self): + """Ask user for filenname and send its contents""" + sys.stderr.write('\n--- File to upload: ') + sys.stderr.flush() + with self.console: + filename = sys.stdin.readline().rstrip('\r\n') + if filename: + try: + with open(filename, 'rb') as f: + sys.stderr.write('--- Sending file {} ---\n'.format(filename)) + while True: + block = f.read(1024) + if not block: + break + self.serial.write(block) + # Wait for output buffer to drain. + self.serial.flush() + sys.stderr.write('.') # Progress indicator. + sys.stderr.write('\n--- File {} sent ---\n'.format(filename)) + except IOError as e: + sys.stderr.write('--- ERROR opening file {}: {} ---\n'.format(filename, e)) + + def change_filter(self): + """change the i/o transformations""" + sys.stderr.write('\n--- Available Filters:\n') + sys.stderr.write('\n'.join( + '--- {:<10} = {.__doc__}'.format(k, v) + for k, v in sorted(TRANSFORMATIONS.items()))) + sys.stderr.write('\n--- Enter new filter name(s) [{}]: '.format(' '.join(self.filters))) + with self.console: + new_filters = sys.stdin.readline().lower().split() + if new_filters: + for f in new_filters: + if f not in TRANSFORMATIONS: + sys.stderr.write('--- unknown filter: {}\n'.format(repr(f))) + break + else: + self.filters = new_filters + self.update_transformations() + sys.stderr.write('--- filters: {}\n'.format(' '.join(self.filters))) + + def change_encoding(self): + """change encoding on the serial port""" + sys.stderr.write('\n--- Enter new encoding name [{}]: '.format(self.input_encoding)) + with self.console: + new_encoding = sys.stdin.readline().strip() + if new_encoding: + try: + codecs.lookup(new_encoding) + except LookupError: + sys.stderr.write('--- invalid encoding name: {}\n'.format(new_encoding)) + else: + self.set_rx_encoding(new_encoding) + self.set_tx_encoding(new_encoding) + sys.stderr.write('--- serial input encoding: {}\n'.format(self.input_encoding)) + sys.stderr.write('--- serial output encoding: {}\n'.format(self.output_encoding)) + + def change_baudrate(self): + """change the baudrate""" + sys.stderr.write('\n--- Baudrate: ') + sys.stderr.flush() + with self.console: + backup = self.serial.baudrate + try: + self.serial.baudrate = int(sys.stdin.readline().strip()) + except ValueError as e: + sys.stderr.write('--- ERROR setting baudrate: {} ---\n'.format(e)) + self.serial.baudrate = backup + else: + self.dump_port_settings() + + def change_port(self): + """Have a conversation with the user to change the serial port""" + with self.console: + try: + port = ask_for_port() + except KeyboardInterrupt: + port = None + if port and port != self.serial.port: + # reader thread needs to be shut down + self._stop_reader() + # save settings + settings = self.serial.getSettingsDict() + try: + new_serial = serial.serial_for_url(port, do_not_open=True) + # restore settings and open + new_serial.applySettingsDict(settings) + new_serial.rts = self.serial.rts + new_serial.dtr = self.serial.dtr + new_serial.open() + new_serial.break_condition = self.serial.break_condition + except Exception as e: + sys.stderr.write('--- ERROR opening new port: {} ---\n'.format(e)) + new_serial.close() + else: + self.serial.close() + self.serial = new_serial + sys.stderr.write('--- Port changed to: {} ---\n'.format(self.serial.port)) + # and restart the reader thread + self._start_reader() + + def suspend_port(self): + """\ + open port temporarily, allow reconnect, exit and port change to get + out of the loop + """ + # reader thread needs to be shut down + self._stop_reader() + self.serial.close() + sys.stderr.write('\n--- Port closed: {} ---\n'.format(self.serial.port)) + do_change_port = False + while not self.serial.is_open: + sys.stderr.write('--- Quit: {exit} | p: port change | any other key to reconnect ---\n'.format( + exit=key_description(self.exit_character))) + k = self.console.getkey() + if k == self.exit_character: + self.stop() # exit app + break + elif k in 'pP': + do_change_port = True + break + try: + self.serial.open() + except Exception as e: + sys.stderr.write('--- ERROR opening port: {} ---\n'.format(e)) + if do_change_port: + self.change_port() + else: + # and restart the reader thread + self._start_reader() + sys.stderr.write('--- Port opened: {} ---\n'.format(self.serial.port)) + + def get_help_text(self): + """return the help text""" + # help text, starts with blank line! + return """ +--- pySerial ({version}) - miniterm - help +--- +--- {exit:8} Exit program +--- {menu:8} Menu escape key, followed by: +--- Menu keys: +--- {menu:7} Send the menu character itself to remote +--- {exit:7} Send the exit character itself to remote +--- {info:7} Show info +--- {upload:7} Upload file (prompt will be shown) +--- {repr:7} encoding +--- {filter:7} edit filters +--- Toggles: +--- {rts:7} RTS {dtr:7} DTR {brk:7} BREAK +--- {echo:7} echo {eol:7} EOL +--- +--- Port settings ({menu} followed by the following): +--- p change port +--- 7 8 set data bits +--- N E O S M change parity (None, Even, Odd, Space, Mark) +--- 1 2 3 set stop bits (1, 2, 1.5) +--- b change baud rate +--- x X disable/enable software flow control +--- r R disable/enable hardware flow control +""".format(version=getattr(serial, 'VERSION', 'unknown version'), + exit=key_description(self.exit_character), + menu=key_description(self.menu_character), + rts=key_description('\x12'), + dtr=key_description('\x04'), + brk=key_description('\x02'), + echo=key_description('\x05'), + info=key_description('\x09'), + upload=key_description('\x15'), + repr=key_description('\x01'), + filter=key_description('\x06'), + eol=key_description('\x0c')) + + +# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +# default args can be used to override when calling main() from an other script +# e.g to create a miniterm-my-device.py +def main(default_port=None, default_baudrate=9600, default_rts=None, default_dtr=None): + """Command line tool, entry point""" + + import argparse + + parser = argparse.ArgumentParser( + description="Miniterm - A simple terminal program for the serial port.") + + parser.add_argument( + "port", + nargs='?', + help="serial port name ('-' to show port list)", + default=default_port) + + parser.add_argument( + "baudrate", + nargs='?', + type=int, + help="set baud rate, default: %(default)s", + default=default_baudrate) + + group = parser.add_argument_group("port settings") + + group.add_argument( + "--parity", + choices=['N', 'E', 'O', 'S', 'M'], + type=lambda c: c.upper(), + help="set parity, one of {N E O S M}, default: N", + default='N') + + group.add_argument( + "--rtscts", + action="store_true", + help="enable RTS/CTS flow control (default off)", + default=False) + + group.add_argument( + "--xonxoff", + action="store_true", + help="enable software flow control (default off)", + default=False) + + group.add_argument( + "--rts", + type=int, + help="set initial RTS line state (possible values: 0, 1)", + default=default_rts) + + group.add_argument( + "--dtr", + type=int, + help="set initial DTR line state (possible values: 0, 1)", + default=default_dtr) + + group.add_argument( + "--ask", + action="store_true", + help="ask again for port when open fails", + default=False) + + group = parser.add_argument_group("data handling") + + group.add_argument( + "-e", "--echo", + action="store_true", + help="enable local echo (default off)", + default=False) + + group.add_argument( + "--encoding", + dest="serial_port_encoding", + metavar="CODEC", + help="set the encoding for the serial port (e.g. hexlify, Latin1, UTF-8), default: %(default)s", + default='UTF-8') + + group.add_argument( + "-f", "--filter", + action="append", + metavar="NAME", + help="add text transformation", + default=[]) + + group.add_argument( + "--eol", + choices=['CR', 'LF', 'CRLF'], + type=lambda c: c.upper(), + help="end of line mode", + default='CRLF') + + group.add_argument( + "--raw", + action="store_true", + help="Do no apply any encodings/transformations", + default=False) + + group = parser.add_argument_group("hotkeys") + + group.add_argument( + "--exit-char", + type=int, + metavar='NUM', + help="Unicode of special character that is used to exit the application, default: %(default)s", + default=0x1d) # GS/CTRL+] + + group.add_argument( + "--menu-char", + type=int, + metavar='NUM', + help="Unicode code of special character that is used to control miniterm (menu), default: %(default)s", + default=0x14) # Menu: CTRL+T + + group = parser.add_argument_group("diagnostics") + + group.add_argument( + "-q", "--quiet", + action="store_true", + help="suppress non-error messages", + default=False) + + group.add_argument( + "--develop", + action="store_true", + help="show Python traceback on error", + default=False) + + args = parser.parse_args() + + if args.menu_char == args.exit_char: + parser.error('--exit-char can not be the same as --menu-char') + + if args.filter: + if 'help' in args.filter: + sys.stderr.write('Available filters:\n') + sys.stderr.write('\n'.join( + '{:<10} = {.__doc__}'.format(k, v) + for k, v in sorted(TRANSFORMATIONS.items()))) + sys.stderr.write('\n') + sys.exit(1) + filters = args.filter + else: + filters = ['default'] + + while True: + # no port given on command line -> ask user now + if args.port is None or args.port == '-': + try: + args.port = ask_for_port() + except KeyboardInterrupt: + sys.stderr.write('\n') + parser.error('user aborted and port is not given') + else: + if not args.port: + parser.error('port is not given') + try: + serial_instance = serial.serial_for_url( + args.port, + args.baudrate, + parity=args.parity, + rtscts=args.rtscts, + xonxoff=args.xonxoff, + do_not_open=True) + + if not hasattr(serial_instance, 'cancel_read'): + # enable timeout for alive flag polling if cancel_read is not available + serial_instance.timeout = 1 + + if args.dtr is not None: + if not args.quiet: + sys.stderr.write('--- forcing DTR {}\n'.format('active' if args.dtr else 'inactive')) + serial_instance.dtr = args.dtr + if args.rts is not None: + if not args.quiet: + sys.stderr.write('--- forcing RTS {}\n'.format('active' if args.rts else 'inactive')) + serial_instance.rts = args.rts + + serial_instance.open() + except serial.SerialException as e: + sys.stderr.write('could not open port {}: {}\n'.format(repr(args.port), e)) + if args.develop: + raise + if not args.ask: + sys.exit(1) + else: + args.port = '-' + else: + break + + miniterm = Miniterm( + serial_instance, + echo=args.echo, + eol=args.eol.lower(), + filters=filters) + miniterm.exit_character = unichr(args.exit_char) + miniterm.menu_character = unichr(args.menu_char) + miniterm.raw = args.raw + miniterm.set_rx_encoding(args.serial_port_encoding) + miniterm.set_tx_encoding(args.serial_port_encoding) + + if not args.quiet: + sys.stderr.write('--- Miniterm on {p.name} {p.baudrate},{p.bytesize},{p.parity},{p.stopbits} ---\n'.format( + p=miniterm.serial)) + sys.stderr.write('--- Quit: {} | Menu: {} | Help: {} followed by {} ---\n'.format( + key_description(miniterm.exit_character), + key_description(miniterm.menu_character), + key_description(miniterm.menu_character), + key_description('\x08'))) + + miniterm.start() + try: + miniterm.join(True) + except KeyboardInterrupt: + pass + if not args.quiet: + sys.stderr.write("\n--- exit ---\n") + miniterm.join() + miniterm.close() + +# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +if __name__ == '__main__': + main() diff --git a/backend/venv/lib/python3.6/site-packages/serial/tools/miniterm.pyc b/backend/venv/lib/python3.6/site-packages/serial/tools/miniterm.pyc new file mode 100644 index 0000000..3e6e5cb Binary files /dev/null and b/backend/venv/lib/python3.6/site-packages/serial/tools/miniterm.pyc differ diff --git a/backend/venv/lib/python3.6/site-packages/serial/urlhandler/__init__.py b/backend/venv/lib/python3.6/site-packages/serial/urlhandler/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/backend/venv/lib/python3.6/site-packages/serial/urlhandler/__init__.pyc b/backend/venv/lib/python3.6/site-packages/serial/urlhandler/__init__.pyc new file mode 100644 index 0000000..17d71fe Binary files /dev/null and b/backend/venv/lib/python3.6/site-packages/serial/urlhandler/__init__.pyc differ diff --git a/backend/venv/lib/python3.6/site-packages/serial/urlhandler/__pycache__/__init__.cpython-36.pyc b/backend/venv/lib/python3.6/site-packages/serial/urlhandler/__pycache__/__init__.cpython-36.pyc new file mode 100644 index 0000000..4ca49b0 Binary files /dev/null and b/backend/venv/lib/python3.6/site-packages/serial/urlhandler/__pycache__/__init__.cpython-36.pyc differ diff --git a/backend/venv/lib/python3.6/site-packages/serial/urlhandler/__pycache__/protocol_alt.cpython-36.pyc b/backend/venv/lib/python3.6/site-packages/serial/urlhandler/__pycache__/protocol_alt.cpython-36.pyc new file mode 100644 index 0000000..feb1675 Binary files /dev/null and b/backend/venv/lib/python3.6/site-packages/serial/urlhandler/__pycache__/protocol_alt.cpython-36.pyc differ diff --git a/backend/venv/lib/python3.6/site-packages/serial/urlhandler/__pycache__/protocol_hwgrep.cpython-36.pyc b/backend/venv/lib/python3.6/site-packages/serial/urlhandler/__pycache__/protocol_hwgrep.cpython-36.pyc new file mode 100644 index 0000000..d0b8a76 Binary files /dev/null and b/backend/venv/lib/python3.6/site-packages/serial/urlhandler/__pycache__/protocol_hwgrep.cpython-36.pyc differ diff --git a/backend/venv/lib/python3.6/site-packages/serial/urlhandler/__pycache__/protocol_loop.cpython-36.pyc b/backend/venv/lib/python3.6/site-packages/serial/urlhandler/__pycache__/protocol_loop.cpython-36.pyc new file mode 100644 index 0000000..41f30ff Binary files /dev/null and b/backend/venv/lib/python3.6/site-packages/serial/urlhandler/__pycache__/protocol_loop.cpython-36.pyc differ diff --git a/backend/venv/lib/python3.6/site-packages/serial/urlhandler/__pycache__/protocol_rfc2217.cpython-36.pyc b/backend/venv/lib/python3.6/site-packages/serial/urlhandler/__pycache__/protocol_rfc2217.cpython-36.pyc new file mode 100644 index 0000000..35c1926 Binary files /dev/null and b/backend/venv/lib/python3.6/site-packages/serial/urlhandler/__pycache__/protocol_rfc2217.cpython-36.pyc differ diff --git a/backend/venv/lib/python3.6/site-packages/serial/urlhandler/__pycache__/protocol_serve-rfc2217.cpython-36.pyc b/backend/venv/lib/python3.6/site-packages/serial/urlhandler/__pycache__/protocol_serve-rfc2217.cpython-36.pyc new file mode 100644 index 0000000..8e4333f Binary files /dev/null and b/backend/venv/lib/python3.6/site-packages/serial/urlhandler/__pycache__/protocol_serve-rfc2217.cpython-36.pyc differ diff --git a/backend/venv/lib/python3.6/site-packages/serial/urlhandler/__pycache__/protocol_socket.cpython-36.pyc b/backend/venv/lib/python3.6/site-packages/serial/urlhandler/__pycache__/protocol_socket.cpython-36.pyc new file mode 100644 index 0000000..445392f Binary files /dev/null and b/backend/venv/lib/python3.6/site-packages/serial/urlhandler/__pycache__/protocol_socket.cpython-36.pyc differ diff --git a/backend/venv/lib/python3.6/site-packages/serial/urlhandler/__pycache__/protocol_spy.cpython-36.pyc b/backend/venv/lib/python3.6/site-packages/serial/urlhandler/__pycache__/protocol_spy.cpython-36.pyc new file mode 100644 index 0000000..5ad8f46 Binary files /dev/null and b/backend/venv/lib/python3.6/site-packages/serial/urlhandler/__pycache__/protocol_spy.cpython-36.pyc differ diff --git a/backend/venv/lib/python3.6/site-packages/serial/urlhandler/protocol_alt.py b/backend/venv/lib/python3.6/site-packages/serial/urlhandler/protocol_alt.py new file mode 100644 index 0000000..c14a87e --- /dev/null +++ b/backend/venv/lib/python3.6/site-packages/serial/urlhandler/protocol_alt.py @@ -0,0 +1,55 @@ +#! python +# +# This module implements a special URL handler that allows selecting an +# alternate implementation provided by some backends. +# +# This file is part of pySerial. https://github.com/pyserial/pyserial +# (C) 2015 Chris Liechti +# +# SPDX-License-Identifier: BSD-3-Clause +# +# URL format: alt://port[?option[=value][&option[=value]]] +# options: +# - class=X used class named X instead of Serial +# +# example: +# use poll based implementation on Posix (Linux): +# python -m serial.tools.miniterm alt:///dev/ttyUSB0?class=PosixPollSerial + +try: + import urlparse +except ImportError: + import urllib.parse as urlparse + +import serial + + +def serial_class_for_url(url): + """extract host and port from an URL string""" + parts = urlparse.urlsplit(url) + if parts.scheme != 'alt': + raise serial.SerialException( + 'expected a string in the form "alt://port[?option[=value][&option[=value]]]": ' + 'not starting with alt:// ({!r})'.format(parts.scheme)) + class_name = 'Serial' + try: + for option, values in urlparse.parse_qs(parts.query, True).items(): + if option == 'class': + class_name = values[0] + else: + raise ValueError('unknown option: {!r}'.format(option)) + except ValueError as e: + raise serial.SerialException( + 'expected a string in the form ' + '"alt://port[?option[=value][&option[=value]]]": {!r}'.format(e)) + if not hasattr(serial, class_name): + raise ValueError('unknown class: {!r}'.format(class_name)) + cls = getattr(serial, class_name) + if not issubclass(cls, serial.Serial): + raise ValueError('class {!r} is not an instance of Serial'.format(class_name)) + return (''.join([parts.netloc, parts.path]), cls) + +# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +if __name__ == '__main__': + s = serial.serial_for_url('alt:///dev/ttyS0?class=PosixPollSerial') + print(s) diff --git a/backend/venv/lib/python3.6/site-packages/serial/urlhandler/protocol_hwgrep.py b/backend/venv/lib/python3.6/site-packages/serial/urlhandler/protocol_hwgrep.py new file mode 100644 index 0000000..49bbebe --- /dev/null +++ b/backend/venv/lib/python3.6/site-packages/serial/urlhandler/protocol_hwgrep.py @@ -0,0 +1,89 @@ +#! python +# +# This module implements a special URL handler that uses the port listing to +# find ports by searching the string descriptions. +# +# This file is part of pySerial. https://github.com/pyserial/pyserial +# (C) 2011-2015 Chris Liechti +# +# SPDX-License-Identifier: BSD-3-Clause +# +# URL format: hwgrep://&