From 5d9f5a99e726794e159909b170e08b8d1033fbb2 Mon Sep 17 00:00:00 2001 From: Patrick Van Oosterwijck Date: Wed, 6 Jan 2021 17:09:03 -0700 Subject: [PATCH 1/7] Add WSGI web server, socket listen, fix bugs - Added WSGI web server with up to 7 simultaneous connections, which are serviced sequentially - adafruit_wiznet5k_wsgiserver based on and has interface identical to adafruit_esp32spi_wsgiserver for maximum compatibility - Added socket 'listen' function - Fixed socket 'connected' logic and made compatible with 'listen' - Fixed source port > 65535 bug - Removed SOCKETS list, it was buggy and caused get_socket allocation to only work if socket connections ended in order, which is not the case for web servers (and wrong in many other cases) - Fixed TX buffer pointer limit to 16-bit - Allow 5s for DHCP response --- README.rst | 65 +++++- adafruit_wiznet5k/adafruit_wiznet5k.py | 47 ++++- adafruit_wiznet5k/adafruit_wiznet5k_dhcp.py | 2 +- adafruit_wiznet5k/adafruit_wiznet5k_dns.py | 0 adafruit_wiznet5k/adafruit_wiznet5k_socket.py | 61 +++--- .../adafruit_wiznet5k_wsgiserver.py | 185 ++++++++++++++++++ 6 files changed, 319 insertions(+), 41 deletions(-) mode change 100755 => 100644 adafruit_wiznet5k/adafruit_wiznet5k.py mode change 100755 => 100644 adafruit_wiznet5k/adafruit_wiznet5k_dhcp.py mode change 100755 => 100644 adafruit_wiznet5k/adafruit_wiznet5k_dns.py mode change 100755 => 100644 adafruit_wiznet5k/adafruit_wiznet5k_socket.py create mode 100644 adafruit_wiznet5k/adafruit_wiznet5k_wsgiserver.py diff --git a/README.rst b/README.rst index 33a2931..75e8727 100644 --- a/README.rst +++ b/README.rst @@ -102,6 +102,69 @@ wifitest.adafruit.com. print("Done!") +This example demonstrates a simple web server that allows setting the Neopixel color. + +.. code-block:: python + + import board + import busio + import digitalio + import neopixel + + from adafruit_wiznet5k.adafruit_wiznet5k import WIZNET5K + import adafruit_wiznet5k.adafruit_wiznet5k_wsgiserver as server + from adafruit_wsgi.wsgi_app import WSGIApp + + print("Wiznet5k Web Server Test") + + # Status LED + led = neopixel.NeoPixel(board.NEOPIXEL, 1) + led.brightness = 0.3 + led[0] = (0, 0, 255) + + # W5500 connections + cs = digitalio.DigitalInOut(board.D10) + spi_bus = busio.SPI(board.SCK, MOSI=board.MOSI, MISO=board.MISO) + + # Initialize ethernet interface with DHCP and the MAC we have from the 24AA02E48 + eth = WIZNET5K(spi_bus, cs) + + # Here we create our application, registering the + # following functions to be called on specific HTTP GET requests routes + + web_app = WSGIApp() + + + @web_app.route("/led///") + def led_on(request, r, g, b): + print("led handler") + led.fill((int(r), int(g), int(b))) + return ("200 OK", [], ["led set!"]) + + @web_app.route("/") + def root(request): + print("root handler") + return ("200 OK", [], ["root document"]) + + @web_app.route("/large") + def large(request): + print("large handler") + return ("200 OK", [], ["*-.-" * 2000]) + + + # Here we setup our server, passing in our web_app as the application + server.set_interface(eth) + wsgiServer = server.WSGIServer(80, application=web_app) + + print("Open this IP in your browser: ", eth.pretty_ip(eth.ip_address)) + + # Start the server + wsgiServer.start() + while True: + # Our main loop where we have the server poll for incoming requests + wsgiServer.update_poll() + # Could do any other background tasks here, like reading sensors + Contributing ============ @@ -122,4 +185,4 @@ with `CircuitPython `_ and made changes so it works 5500 Ethernet interface `_ and CPython's `Socket low-level networking interface module `_. -This open source code is licensed under the LGPL license (see LICENSE for details). \ No newline at end of file +This open source code is licensed under the LGPL license (see LICENSE for details). diff --git a/adafruit_wiznet5k/adafruit_wiznet5k.py b/adafruit_wiznet5k/adafruit_wiznet5k.py old mode 100755 new mode 100644 index 6b7dbd7..99c81df --- a/adafruit_wiznet5k/adafruit_wiznet5k.py +++ b/adafruit_wiznet5k/adafruit_wiznet5k.py @@ -5,6 +5,7 @@ # Copyright (c) 2008 Bjoern Hartmann # Copyright 2018 Paul Stoffregen # Modified by Brent Rubell for Adafruit Industries, 2020 +# Copyright (c) 2021 Patrick Van Oosterwijck # # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the "Software"), to deal @@ -29,7 +30,8 @@ Pure-Python interface for WIZNET 5k ethernet modules. -* Author(s): WIZnet, Arduino LLC, Bjoern Hartmann, Paul Stoffregen, Brent Rubell +* Author(s): WIZnet, Arduino LLC, Bjoern Hartmann, Paul Stoffregen, Brent Rubell, + Patrick Van Oosterwijck Implementation Notes -------------------- @@ -257,7 +259,7 @@ def get_host_by_name(self, hostname): print("* Get host by name") if isinstance(hostname, str): hostname = bytes(hostname, "utf-8") - self._src_port = int(time.monotonic()) + self._src_port = int(time.monotonic()) & 0xFFFF # Return IP assigned by DHCP _dns_client = dns.DNS(self, self._dns, debug=self._debug) ret = _dns_client.gethostbyname(hostname) @@ -563,16 +565,15 @@ def _send_socket_cmd(self, socket, cmd): if self._debug: print("waiting for sncr to clear...") - def get_socket(self, sockets): + def get_socket(self): """Requests, allocates and returns a socket from the W5k chip. Returned socket number may not exceed max_sockets. - :parm int socket_num: Desired socket number """ if self._debug: print("*** Get socket") sock = 0 - for _sock in range(len(sockets), self.max_sockets): + for _sock in range(self.max_sockets): status = self.socket_status(_sock) if ( status[0] == SNSR_SOCK_CLOSED @@ -586,6 +587,33 @@ def get_socket(self, sockets): print("Allocated socket #{}".format(sock)) return sock + def socket_listen(self, socket_num, port): + """Start listening on a socket (TCP mode only). + :parm int socket_num: socket number + :parm int port: port to listen on + """ + assert self.link_status, "Ethernet cable disconnected!" + if self._debug: + print( + "* Listening on port={}, ip={}".format( + port, self.pretty_ip(self.ip_address) + ) + ) + # Initialize a socket and set the mode + self._src_port = port + res = self.socket_open(socket_num, conn_mode=SNMR_TCP) + if res == 1: + raise RuntimeError("Failed to initalize the socket.") + # Send listen command + self._send_socket_cmd(socket_num, CMD_SOCK_LISTEN) + # Wait until ready + status = [SNSR_SOCK_CLOSED] + while status[0] != SNSR_SOCK_LISTEN: + status = self._read_snsr(socket_num) + if status[0] == SNSR_SOCK_CLOSED: + raise RuntimeError("Listening socket closed.") + + def socket_open(self, socket_num, conn_mode=SNMR_TCP): """Opens a TCP or UDP socket. By default, we use 'conn_mode'=SNMR_TCP but we may also use SNMR_UDP. @@ -593,7 +621,8 @@ def socket_open(self, socket_num, conn_mode=SNMR_TCP): assert self.link_status, "Ethernet cable disconnected!" if self._debug: print("*** Opening socket %d" % socket_num) - if self._read_snsr(socket_num)[0] == SNSR_SOCK_CLOSED: + status = self._read_snsr(socket_num)[0] + if status in (SNSR_SOCK_CLOSED, SNSR_SOCK_FIN_WAIT, SNSR_SOCK_CLOSE_WAIT): if self._debug: print("* Opening W5k Socket, protocol={}".format(conn_mode)) time.sleep(0.00025) @@ -719,7 +748,7 @@ def socket_write(self, socket_num, buffer): dst_addr = offset + (socket_num * 2048 + 0x8000) # update sn_tx_wr to the value + data size - ptr += len(buffer) + ptr = (ptr + len(buffer)) & 0xFFFF self._write_sntx_wr(socket_num, ptr) cntl_byte = 0x14 + (socket_num << 5) @@ -826,12 +855,10 @@ def _read_sncr(self, sock): def _read_snmr(self, sock): return self._read_socket(sock, REG_SNMR) - def _write_socket(self, sock, address, data, length=None): + def _write_socket(self, sock, address, data): """Write to a W5k socket register.""" base = self._ch_base_msb << 8 cntl_byte = (sock << 5) + 0x0C - if length is None: - return self.write(base + sock * CH_SIZE + address, cntl_byte, data) return self.write(base + sock * CH_SIZE + address, cntl_byte, data) def _read_socket(self, sock, address): diff --git a/adafruit_wiznet5k/adafruit_wiznet5k_dhcp.py b/adafruit_wiznet5k/adafruit_wiznet5k_dhcp.py old mode 100755 new mode 100644 index ea41d02..e551a38 --- a/adafruit_wiznet5k/adafruit_wiznet5k_dhcp.py +++ b/adafruit_wiznet5k/adafruit_wiznet5k_dhcp.py @@ -101,7 +101,7 @@ class DHCP: # pylint: disable=too-many-arguments, too-many-instance-attributes, invalid-name def __init__( - self, eth, mac_address, hostname=None, response_timeout=3, debug=False + self, eth, mac_address, hostname=None, response_timeout=5, debug=False ): self._debug = debug self._response_timeout = response_timeout diff --git a/adafruit_wiznet5k/adafruit_wiznet5k_dns.py b/adafruit_wiznet5k/adafruit_wiznet5k_dns.py old mode 100755 new mode 100644 diff --git a/adafruit_wiznet5k/adafruit_wiznet5k_socket.py b/adafruit_wiznet5k/adafruit_wiznet5k_socket.py old mode 100755 new mode 100644 index ea2cb0f..66b6a36 --- a/adafruit_wiznet5k/adafruit_wiznet5k_socket.py +++ b/adafruit_wiznet5k/adafruit_wiznet5k_socket.py @@ -2,6 +2,7 @@ # # Copyright (c) 2019 ladyada for Adafruit Industries # Modified by Brent Rubell for Adafruit Industries, 2020 +# Copyright (c) 2020 Patrick Van Oosterwijck # # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the "Software"), to deal @@ -26,7 +27,7 @@ A socket compatible interface with the Wiznet5k module. -* Author(s): ladyada, Brent Rubell +* Author(s): ladyada, Brent Rubell, Patrick Van Oosterwijck """ import gc @@ -64,8 +65,6 @@ def htons(x): AF_INET = const(3) NO_SOCKET_AVAIL = const(255) -# keep track of sockets we allocate -SOCKETS = [] # pylint: disable=too-many-arguments, unused-argument def getaddrinfo(host, port, family=0, socktype=0, proto=0, flags=0): @@ -122,9 +121,7 @@ def __init__( self._buffer = b"" self._timeout = 0 - self._socknum = _the_interface.get_socket(SOCKETS) - SOCKETS.append(self._socknum) - self.settimeout(self._timeout) + self._socknum = _the_interface.get_socket() @property def socknum(self): @@ -135,22 +132,20 @@ def socknum(self): def connected(self): """Returns whether or not we are connected to the socket.""" if self.socknum >= _the_interface.max_sockets: - return 0 + return False status = _the_interface.socket_status(self.socknum)[0] - if ( - status == adafruit_wiznet5k.SNSR_SOCK_CLOSE_WAIT - and self.available()[0] == 0 - ): + if (status == adafruit_wiznet5k.SNSR_SOCK_CLOSE_WAIT + and self.available() == 0): result = False - result = status not in ( - adafruit_wiznet5k.SNSR_SOCK_CLOSED, - adafruit_wiznet5k.SNSR_SOCK_LISTEN, - adafruit_wiznet5k.SNSR_SOCK_CLOSE_WAIT, - adafruit_wiznet5k.SNSR_SOCK_FIN_WAIT, - ) - if not result: + else: + result = status not in ( + adafruit_wiznet5k.SNSR_SOCK_CLOSED, + adafruit_wiznet5k.SNSR_SOCK_LISTEN, + adafruit_wiznet5k.SNSR_SOCK_TIME_WAIT, + adafruit_wiznet5k.SNSR_SOCK_FIN_WAIT, + ) + if not result and status != adafruit_wiznet5k.SNSR_SOCK_LISTEN: self.close() - return result return result def getpeername(self): @@ -167,6 +162,13 @@ def inet_aton(self, ip_string): self._buffer = bytearray(self._buffer) return self._buffer + def listen(self, port): + """Listen on the specified port. + :param int port: The port to listen on. + """ + _the_interface.socket_listen(self.socknum, port) + self._buffer = b"" + def connect(self, address, conntype=None): """Connect to a remote socket at address. (The format of address depends on the address family — see above.) @@ -209,11 +211,11 @@ def recv(self, bufsize=0): # pylint: disable=too-many-branches avail = _the_interface.udp_remaining() if avail: if self._sock_type == SOCK_STREAM: - self._buffer += _the_interface.socket_read(self.socknum, avail)[ - 1 - ] + self._buffer += _the_interface.socket_read( + self.socknum, avail)[1] elif self._sock_type == SOCK_DGRAM: - self._buffer += _the_interface.read_udp(self.socknum, avail)[1] + self._buffer += _the_interface.read_udp( + self.socknum, avail)[1] else: break gc.collect() @@ -235,10 +237,10 @@ def recv(self, bufsize=0): # pylint: disable=too-many-branches stamp = time.monotonic() if self._sock_type == SOCK_STREAM: recv = _the_interface.socket_read( - self.socknum, min(to_read, avail) - )[1] + self.socknum, min(to_read, avail))[1] elif self._sock_type == SOCK_DGRAM: - recv = _the_interface.read_udp(self.socknum, min(to_read, avail))[1] + recv = _the_interface.read_udp( + self.socknum, min(to_read, avail))[1] recv = bytes(recv) received.append(recv) to_read -= len(recv) @@ -267,12 +269,14 @@ def readline(self): if self._sock_type == SOCK_STREAM: avail = self.available() if avail: - self._buffer += _the_interface.socket_read(self.socknum, avail)[1] + self._buffer += _the_interface.socket_read( + self.socknum, avail)[1] elif self._sock_type == SOCK_DGRAM: avail = _the_interface.udp_remaining() if avail: self._buffer += _the_interface.read_udp(self.socknum, avail) - elif self._timeout > 0 and time.monotonic() - stamp > self._timeout: + if not avail and self._timeout > 0 and \ + time.monotonic() - stamp > self._timeout: self.close() raise RuntimeError("Didn't receive response, failing out...") firstline, self._buffer = self._buffer.split(b"\r\n", 1) @@ -287,7 +291,6 @@ def disconnect(self): def close(self): """Closes the socket.""" _the_interface.socket_close(self.socknum) - SOCKETS.remove(self.socknum) def available(self): """Returns how many bytes of data are available to be read from the socket.""" diff --git a/adafruit_wiznet5k/adafruit_wiznet5k_wsgiserver.py b/adafruit_wiznet5k/adafruit_wiznet5k_wsgiserver.py new file mode 100644 index 0000000..3890ffb --- /dev/null +++ b/adafruit_wiznet5k/adafruit_wiznet5k_wsgiserver.py @@ -0,0 +1,185 @@ +# Based on ESP32 code Copyright (c) 2019 Matt Costi for Adafruit Industries +# SPDX-FileCopyrightText: Copyright (c) 2020 Patrick Van Oosterwijck +# +# SPDX-License-Identifier: MIT + +""" +`adafruit_wiznet5k_wsgiserver` +================================================================================ + +A simple WSGI (Web Server Gateway Interface) server that interfaces with the W5500. +Opens a listening port on the W5500 to listen for incoming HTTP Requests and +Accepts an Application object that must be callable, which gets called +whenever a new HTTP Request has been received. + +The Application MUST accept 2 ordered parameters: + 1. environ object (incoming request data) + 2. start_response function. Must be called before the Application + callable returns, in order to set the response status and headers. + +The Application MUST return strings in a list, which is the response data + +Requires update_poll being called in the applications main event loop. + +For more details about Python WSGI see: +https://www.python.org/dev/peps/pep-0333/ + +* Author(s): Matt Costi, Patrick Van Oosterwijck +""" +# pylint: disable=no-name-in-module + +import io +import gc +from micropython import const +import adafruit_wiznet5k.adafruit_wiznet5k_socket as socket + +_the_interface = None # pylint: disable=invalid-name + + +def set_interface(iface): + """Helper to set the global internet interface""" + global _the_interface # pylint: disable=global-statement, invalid-name + _the_interface = iface + socket.set_interface(iface) + + +# Maximum number of sockets for the web server, leave one for other things +MAX_SOCK_NUM = const(7) + +# pylint: disable=invalid-name +class WSGIServer: + """ + A simple server that implements the WSGI interface + """ + + def __init__(self, port=80, debug=False, application=None): + self.application = application + self.port = port + self._client_sock = [] + self._debug = debug + + self._response_status = None + self._response_headers = [] + + def start(self): + """ + Starts the server and begins listening for incoming connections. + Call update_poll in the main loop for the application callable to be + invoked on receiving an incoming request. + """ + for _ in range(MAX_SOCK_NUM): + new_sock = socket.socket() + new_sock.listen(self.port) + self._client_sock.append(new_sock) + if self._debug: + ip = _the_interface.pretty_ip(_the_interface.ip_address) + print("Server available at {0}:{1}".format(ip, self.port)) + + def update_poll(self): + """ + Call this method inside your main event loop to get the server + check for new incoming client requests. When a request comes in, + the application callable will be invoked. + """ + add_sock = [] + for sock in self._client_sock: + if sock.available(): + environ = self._get_environ(sock) + result = self.application(environ, self._start_response) + self.finish_response(result, sock) + self._client_sock.remove(sock) + new_sock = socket.socket() + new_sock.listen(self.port) + add_sock.append(new_sock) + self._client_sock.extend(add_sock) + + def finish_response(self, result, client): + """ + Called after the application callable returns result data to respond with. + Creates the HTTP Response payload from the response_headers and results data, + and sends it back to client. + + :param string result: the data string to send back in the response to the client. + :param Socket client: the socket to send the response to. + """ + try: + response = "HTTP/1.1 {0}\r\n".format(self._response_status) + for header in self._response_headers: + response += "{0}: {1}\r\n".format(*header) + response += "\r\n" + client.send(response.encode("utf-8")) + for data in result: + if isinstance(data, bytes): + client.send(data) + else: + client.send(data.encode("utf-8")) + gc.collect() + finally: + client.disconnect() + client.close() + + def _start_response(self, status, response_headers): + """ + The application callable will be given this method as the second param + This is to be called before the application callable returns, to signify + the response can be started with the given status and headers. + + :param string status: a status string including the code and reason. ex: "200 OK" + :param list response_headers: a list of tuples to represent the headers. + ex ("header-name", "header value") + """ + self._response_status = status + self._response_headers = [("Server", "w5kWSGIServer")] + response_headers + + def _get_environ(self, client): + """ + The application callable will be given the resulting environ dictionary. + It contains metadata about the incoming request and the request body ("wsgi.input") + + :param Socket client: socket to read the request from + """ + env = {} + line = str(client.readline(), "utf-8") + (method, path, ver) = line.rstrip("\r\n").split(None, 2) + + env["wsgi.version"] = (1, 0) + env["wsgi.url_scheme"] = "http" + env["wsgi.multithread"] = False + env["wsgi.multiprocess"] = False + env["wsgi.run_once"] = False + + env["REQUEST_METHOD"] = method + env["SCRIPT_NAME"] = "" + env["SERVER_NAME"] = _the_interface.pretty_ip(_the_interface.ip_address) + env["SERVER_PROTOCOL"] = ver + env["SERVER_PORT"] = self.port + if path.find("?") >= 0: + env["PATH_INFO"] = path.split("?")[0] + env["QUERY_STRING"] = path.split("?")[1] + else: + env["PATH_INFO"] = path + + headers = {} + while True: + header = str(client.readline(), "utf-8") + if header == "": + break + title, content = header.split(": ", 1) + headers[title.lower()] = content + + if "content-type" in headers: + env["CONTENT_TYPE"] = headers.get("content-type") + if "content-length" in headers: + env["CONTENT_LENGTH"] = headers.get("content-length") + body = client.recv(int(env["CONTENT_LENGTH"])) + env["wsgi.input"] = io.StringIO(body) + else: + body = client.recv() + env["wsgi.input"] = io.StringIO(body) + for name, value in headers.items(): + key = "HTTP_" + name.replace("-", "_").upper() + if key in env: + value = "{0},{1}".format(env[key], value) + env[key] = value + + return env From f2022730f51fbc4ac5c5fdaa380d9dc3a04f6147 Mon Sep 17 00:00:00 2001 From: Patrick Van Oosterwijck Date: Fri, 8 Jan 2021 12:17:30 -0700 Subject: [PATCH 2/7] Bug fix to prevent the server from hanging when client disconnects - Server would hang and not serve any other clients when a client disconnected, fixed in `socket_write`. - Fixed `_get_tx_free_size` bug that would always return 0 and `socket_write` that used this function in a useless way and didn't actually check anything because it checked whether the returned bytearray was in a tuple of status ints. - `_get_tx_free_size` and `_get_rx_rcv_size` now return int instead of bytearray as you'd expect. --- adafruit_wiznet5k/adafruit_wiznet5k.py | 22 ++++++++++------------ 1 file changed, 10 insertions(+), 12 deletions(-) diff --git a/adafruit_wiznet5k/adafruit_wiznet5k.py b/adafruit_wiznet5k/adafruit_wiznet5k.py index 99c81df..e0cf213 100644 --- a/adafruit_wiznet5k/adafruit_wiznet5k.py +++ b/adafruit_wiznet5k/adafruit_wiznet5k.py @@ -494,7 +494,6 @@ def socket_available(self, socket_num, sock_type=SNMR_TCP): res = self._get_rx_rcv_size(socket_num) - res = int.from_bytes(res, "b") if sock_type == SNMR_TCP: return res if res > 0: @@ -574,11 +573,9 @@ def get_socket(self): sock = 0 for _sock in range(self.max_sockets): - status = self.socket_status(_sock) - if ( - status[0] == SNSR_SOCK_CLOSED - or status[0] == SNSR_SOCK_FIN_WAIT - or status[0] == SNSR_SOCK_CLOSE_WAIT + status = self.socket_status(_sock)[0] + if (status in + (SNSR_SOCK_CLOSED, SNSR_SOCK_FIN_WAIT, SNSR_SOCK_CLOSE_WAIT) ): sock = _sock break @@ -670,7 +667,6 @@ def socket_read(self, socket_num, length): # Check if there is data available on the socket ret = self._get_rx_rcv_size(socket_num) - ret = int.from_bytes(ret, "b") if self._debug: print("Bytes avail. on sock: ", ret) if ret == 0: @@ -737,7 +733,7 @@ def socket_write(self, socket_num, buffer): free_size = self._get_tx_free_size(socket_num) while free_size < ret: free_size = self._get_tx_free_size(socket_num) - status = self.socket_status(socket_num) + status = self.socket_status(socket_num)[0] if status not in (SNSR_SOCK_ESTABLISHED, SNSR_SOCK_CLOSE_WAIT): ret = 0 break @@ -761,7 +757,9 @@ def socket_write(self, socket_num, buffer): while ( self._read_socket(socket_num, REG_SNIR)[0] & SNIR_SEND_OK ) != SNIR_SEND_OK: - if self.socket_status(socket_num) == SNSR_SOCK_CLOSED: + if (self.socket_status(socket_num)[0] in + (SNSR_SOCK_CLOSED, SNSR_SOCK_FIN_WAIT, SNSR_SOCK_CLOSE_WAIT) + ): # self.socket_close(socket_num) return 0 time.sleep(0.01) @@ -779,17 +777,17 @@ def _get_rx_rcv_size(self, sock): val_1 = self._read_snrx_rsr(sock) if val_1 != 0: val = self._read_snrx_rsr(sock) - return val + return int.from_bytes(val, "b") def _get_tx_free_size(self, sock): """Get free size of sock's tx buffer block.""" val = 0 - val_1 = 0 + val_1 = self._read_sntx_fsr(sock) while val != val_1: val_1 = self._read_sntx_fsr(sock) if val_1 != 0: val = self._read_sntx_fsr(sock) - return val + return int.from_bytes(val, "b") def _read_snrx_rd(self, sock): self._pbuff[0] = self._read_socket(sock, REG_SNRX_RD)[0] From a465bb0f01b890fb65c868108aa01627d92e2269 Mon Sep 17 00:00:00 2001 From: Patrick Van Oosterwijck Date: Fri, 8 Jan 2021 12:55:07 -0700 Subject: [PATCH 3/7] Split listen to bind/listen for compatibility, formatting fixes - The socket now expects `bind`, then `listen` for improved compatibility with CPython socket. - Fixed formatting to make black happy, even did the extremely ugly thing on line 222 of adafruit_wiznet5k_socket.py. --- adafruit_wiznet5k/adafruit_wiznet5k.py | 13 +++--- adafruit_wiznet5k/adafruit_wiznet5k_socket.py | 43 +++++++++++-------- .../adafruit_wiznet5k_wsgiserver.py | 6 ++- 3 files changed, 36 insertions(+), 26 deletions(-) diff --git a/adafruit_wiznet5k/adafruit_wiznet5k.py b/adafruit_wiznet5k/adafruit_wiznet5k.py index e0cf213..6f5cbba 100644 --- a/adafruit_wiznet5k/adafruit_wiznet5k.py +++ b/adafruit_wiznet5k/adafruit_wiznet5k.py @@ -574,9 +574,7 @@ def get_socket(self): sock = 0 for _sock in range(self.max_sockets): status = self.socket_status(_sock)[0] - if (status in - (SNSR_SOCK_CLOSED, SNSR_SOCK_FIN_WAIT, SNSR_SOCK_CLOSE_WAIT) - ): + if status in (SNSR_SOCK_CLOSED, SNSR_SOCK_FIN_WAIT, SNSR_SOCK_CLOSE_WAIT): sock = _sock break @@ -592,7 +590,7 @@ def socket_listen(self, socket_num, port): assert self.link_status, "Ethernet cable disconnected!" if self._debug: print( - "* Listening on port={}, ip={}".format( + "* Listening on port={}, ip={}".format( port, self.pretty_ip(self.ip_address) ) ) @@ -610,7 +608,6 @@ def socket_listen(self, socket_num, port): if status[0] == SNSR_SOCK_CLOSED: raise RuntimeError("Listening socket closed.") - def socket_open(self, socket_num, conn_mode=SNMR_TCP): """Opens a TCP or UDP socket. By default, we use 'conn_mode'=SNMR_TCP but we may also use SNMR_UDP. @@ -757,8 +754,10 @@ def socket_write(self, socket_num, buffer): while ( self._read_socket(socket_num, REG_SNIR)[0] & SNIR_SEND_OK ) != SNIR_SEND_OK: - if (self.socket_status(socket_num)[0] in - (SNSR_SOCK_CLOSED, SNSR_SOCK_FIN_WAIT, SNSR_SOCK_CLOSE_WAIT) + if self.socket_status(socket_num)[0] in ( + SNSR_SOCK_CLOSED, + SNSR_SOCK_FIN_WAIT, + SNSR_SOCK_CLOSE_WAIT, ): # self.socket_close(socket_num) return 0 diff --git a/adafruit_wiznet5k/adafruit_wiznet5k_socket.py b/adafruit_wiznet5k/adafruit_wiznet5k_socket.py index 66b6a36..36648b2 100644 --- a/adafruit_wiznet5k/adafruit_wiznet5k_socket.py +++ b/adafruit_wiznet5k/adafruit_wiznet5k_socket.py @@ -120,6 +120,7 @@ def __init__( self._sock_type = type self._buffer = b"" self._timeout = 0 + self._listen_port = None self._socknum = _the_interface.get_socket() @@ -134,8 +135,7 @@ def connected(self): if self.socknum >= _the_interface.max_sockets: return False status = _the_interface.socket_status(self.socknum)[0] - if (status == adafruit_wiznet5k.SNSR_SOCK_CLOSE_WAIT - and self.available() == 0): + if status == adafruit_wiznet5k.SNSR_SOCK_CLOSE_WAIT and self.available() == 0: result = False else: result = status not in ( @@ -162,11 +162,18 @@ def inet_aton(self, ip_string): self._buffer = bytearray(self._buffer) return self._buffer - def listen(self, port): - """Listen on the specified port. - :param int port: The port to listen on. + def bind(self, address): + """Bind the socket to the listen port, we ignore the host. + :param tuple address: local socket as a (host, port) tuple, host is ignored. """ - _the_interface.socket_listen(self.socknum, port) + _, self._listen_port = address + + def listen(self, backlog=None): + """Listen on the port specified by bind. + :param backlog: For compatibility but ignored. + """ + assert self._listen_port is not None, "Use bind to set the port before listen!" + _the_interface.socket_listen(self.socknum, self._listen_port) self._buffer = b"" def connect(self, address, conntype=None): @@ -211,11 +218,11 @@ def recv(self, bufsize=0): # pylint: disable=too-many-branches avail = _the_interface.udp_remaining() if avail: if self._sock_type == SOCK_STREAM: - self._buffer += _the_interface.socket_read( - self.socknum, avail)[1] + self._buffer += _the_interface.socket_read(self.socknum, avail)[ + 1 + ] elif self._sock_type == SOCK_DGRAM: - self._buffer += _the_interface.read_udp( - self.socknum, avail)[1] + self._buffer += _the_interface.read_udp(self.socknum, avail)[1] else: break gc.collect() @@ -237,10 +244,10 @@ def recv(self, bufsize=0): # pylint: disable=too-many-branches stamp = time.monotonic() if self._sock_type == SOCK_STREAM: recv = _the_interface.socket_read( - self.socknum, min(to_read, avail))[1] + self.socknum, min(to_read, avail) + )[1] elif self._sock_type == SOCK_DGRAM: - recv = _the_interface.read_udp( - self.socknum, min(to_read, avail))[1] + recv = _the_interface.read_udp(self.socknum, min(to_read, avail))[1] recv = bytes(recv) received.append(recv) to_read -= len(recv) @@ -269,14 +276,16 @@ def readline(self): if self._sock_type == SOCK_STREAM: avail = self.available() if avail: - self._buffer += _the_interface.socket_read( - self.socknum, avail)[1] + self._buffer += _the_interface.socket_read(self.socknum, avail)[1] elif self._sock_type == SOCK_DGRAM: avail = _the_interface.udp_remaining() if avail: self._buffer += _the_interface.read_udp(self.socknum, avail) - if not avail and self._timeout > 0 and \ - time.monotonic() - stamp > self._timeout: + if ( + not avail + and self._timeout > 0 + and time.monotonic() - stamp > self._timeout + ): self.close() raise RuntimeError("Didn't receive response, failing out...") firstline, self._buffer = self._buffer.split(b"\r\n", 1) diff --git a/adafruit_wiznet5k/adafruit_wiznet5k_wsgiserver.py b/adafruit_wiznet5k/adafruit_wiznet5k_wsgiserver.py index 3890ffb..14c3de2 100644 --- a/adafruit_wiznet5k/adafruit_wiznet5k_wsgiserver.py +++ b/adafruit_wiznet5k/adafruit_wiznet5k_wsgiserver.py @@ -69,7 +69,8 @@ def start(self): """ for _ in range(MAX_SOCK_NUM): new_sock = socket.socket() - new_sock.listen(self.port) + new_sock.bind((None, self.port)) + new_sock.listen() self._client_sock.append(new_sock) if self._debug: ip = _the_interface.pretty_ip(_the_interface.ip_address) @@ -89,7 +90,8 @@ def update_poll(self): self.finish_response(result, sock) self._client_sock.remove(sock) new_sock = socket.socket() - new_sock.listen(self.port) + new_sock.bind((None, self.port)) + new_sock.listen() add_sock.append(new_sock) self._client_sock.extend(add_sock) From 02e08b3b4e954479bccc41f1f0539673c7206737 Mon Sep 17 00:00:00 2001 From: Patrick Van Oosterwijck Date: Mon, 11 Jan 2021 16:47:26 -0700 Subject: [PATCH 4/7] Timeout for send and other fixes - Socket send now has a timeout so we can eventually release the socket in situations where we are trying to send but the network connection died so there was no TCP handshake to close the connection. - `WSGIServer` now has a 20s timeout for its sockets. - DHCP timeout increased to 30s in accordance with RFC3315. - Recognize more states as "socket is closed and available". - `get_socket` now returns an invalid value if no free socket is available, which will cause an exception in the socket constructor. It used to return 0 if no sockets were available, which is a valid socket value, and caused all kinds of problems for the rightful owner of socket 0. --- adafruit_wiznet5k/adafruit_wiznet5k.py | 32 +++++++++++++++---- adafruit_wiznet5k/adafruit_wiznet5k_dhcp.py | 2 +- adafruit_wiznet5k/adafruit_wiznet5k_socket.py | 6 ++-- .../adafruit_wiznet5k_wsgiserver.py | 7 ++-- 4 files changed, 35 insertions(+), 12 deletions(-) diff --git a/adafruit_wiznet5k/adafruit_wiznet5k.py b/adafruit_wiznet5k/adafruit_wiznet5k.py index 6f5cbba..16b8402 100644 --- a/adafruit_wiznet5k/adafruit_wiznet5k.py +++ b/adafruit_wiznet5k/adafruit_wiznet5k.py @@ -132,6 +132,7 @@ # Maximum number of sockets to support, differs between chip versions. W5200_W5500_MAX_SOCK_NUM = const(0x08) +SOCKET_INVALID = const(255) # UDP socket struct. UDP_SOCK = {"bytes_remaining": 0, "remote_ip": 0, "remote_port": 0} @@ -163,7 +164,7 @@ def __init__( is_dhcp=True, mac=DEFAULT_MAC, hostname=None, - dhcp_timeout=3, + dhcp_timeout=30, debug=False, ): self._debug = debug @@ -571,10 +572,16 @@ def get_socket(self): if self._debug: print("*** Get socket") - sock = 0 + sock = SOCKET_INVALID for _sock in range(self.max_sockets): status = self.socket_status(_sock)[0] - if status in (SNSR_SOCK_CLOSED, SNSR_SOCK_FIN_WAIT, SNSR_SOCK_CLOSE_WAIT): + if status in ( + SNSR_SOCK_CLOSED, + SNSR_SOCK_TIME_WAIT, + SNSR_SOCK_FIN_WAIT, + SNSR_SOCK_CLOSE_WAIT, + SNSR_SOCK_CLOSING, + ): sock = _sock break @@ -616,7 +623,13 @@ def socket_open(self, socket_num, conn_mode=SNMR_TCP): if self._debug: print("*** Opening socket %d" % socket_num) status = self._read_snsr(socket_num)[0] - if status in (SNSR_SOCK_CLOSED, SNSR_SOCK_FIN_WAIT, SNSR_SOCK_CLOSE_WAIT): + if status in ( + SNSR_SOCK_CLOSED, + SNSR_SOCK_TIME_WAIT, + SNSR_SOCK_FIN_WAIT, + SNSR_SOCK_CLOSE_WAIT, + SNSR_SOCK_CLOSING, + ): if self._debug: print("* Opening W5k Socket, protocol={}".format(conn_mode)) time.sleep(0.00025) @@ -714,7 +727,7 @@ def read_udp(self, socket_num, length): return ret, resp return -1 - def socket_write(self, socket_num, buffer): + def socket_write(self, socket_num, buffer, timeout=0): """Writes a bytearray to a provided socket.""" assert self.link_status, "Ethernet cable disconnected!" assert socket_num <= self.max_sockets, "Provided socket exceeds max_sockets." @@ -725,13 +738,16 @@ def socket_write(self, socket_num, buffer): ret = SOCK_SIZE else: ret = len(buffer) + stamp = time.monotonic() # if buffer is available, start the transfer free_size = self._get_tx_free_size(socket_num) while free_size < ret: free_size = self._get_tx_free_size(socket_num) status = self.socket_status(socket_num)[0] - if status not in (SNSR_SOCK_ESTABLISHED, SNSR_SOCK_CLOSE_WAIT): + if status not in (SNSR_SOCK_ESTABLISHED, SNSR_SOCK_CLOSE_WAIT) or ( + timeout and time.monotonic() - stamp > timeout + ): ret = 0 break @@ -756,9 +772,11 @@ def socket_write(self, socket_num, buffer): ) != SNIR_SEND_OK: if self.socket_status(socket_num)[0] in ( SNSR_SOCK_CLOSED, + SNSR_SOCK_TIME_WAIT, SNSR_SOCK_FIN_WAIT, SNSR_SOCK_CLOSE_WAIT, - ): + SNSR_SOCK_CLOSING, + ) or (timeout and time.monotonic() - stamp > timeout): # self.socket_close(socket_num) return 0 time.sleep(0.01) diff --git a/adafruit_wiznet5k/adafruit_wiznet5k_dhcp.py b/adafruit_wiznet5k/adafruit_wiznet5k_dhcp.py index e551a38..975111c 100644 --- a/adafruit_wiznet5k/adafruit_wiznet5k_dhcp.py +++ b/adafruit_wiznet5k/adafruit_wiznet5k_dhcp.py @@ -101,7 +101,7 @@ class DHCP: # pylint: disable=too-many-arguments, too-many-instance-attributes, invalid-name def __init__( - self, eth, mac_address, hostname=None, response_timeout=5, debug=False + self, eth, mac_address, hostname=None, response_timeout=30, debug=False ): self._debug = debug self._response_timeout = response_timeout diff --git a/adafruit_wiznet5k/adafruit_wiznet5k_socket.py b/adafruit_wiznet5k/adafruit_wiznet5k_socket.py index 36648b2..2d94f56 100644 --- a/adafruit_wiznet5k/adafruit_wiznet5k_socket.py +++ b/adafruit_wiznet5k/adafruit_wiznet5k_socket.py @@ -63,7 +63,7 @@ def htons(x): TCP_MODE = 80 SOCK_DGRAM = const(0x02) # UDP AF_INET = const(3) -NO_SOCKET_AVAIL = const(255) +SOCKET_INVALID = const(255) # pylint: disable=too-many-arguments, unused-argument @@ -123,6 +123,8 @@ def __init__( self._listen_port = None self._socknum = _the_interface.get_socket() + if self._socknum == SOCKET_INVALID: + raise RuntimeError("Failed to allocate socket.") @property def socknum(self): @@ -201,7 +203,7 @@ def send(self, data): :param bytearray data: Desired data to send to the socket. """ - _the_interface.socket_write(self.socknum, data) + _the_interface.socket_write(self.socknum, data, self._timeout) gc.collect() def recv(self, bufsize=0): # pylint: disable=too-many-branches diff --git a/adafruit_wiznet5k/adafruit_wiznet5k_wsgiserver.py b/adafruit_wiznet5k/adafruit_wiznet5k_wsgiserver.py index 14c3de2..5104c0f 100644 --- a/adafruit_wiznet5k/adafruit_wiznet5k_wsgiserver.py +++ b/adafruit_wiznet5k/adafruit_wiznet5k_wsgiserver.py @@ -43,8 +43,8 @@ def set_interface(iface): socket.set_interface(iface) -# Maximum number of sockets for the web server, leave one for other things -MAX_SOCK_NUM = const(7) +# Maximum number of sockets for the web server (number of connections we can hold) +MAX_SOCK_NUM = const(6) # pylint: disable=invalid-name class WSGIServer: @@ -55,6 +55,7 @@ class WSGIServer: def __init__(self, port=80, debug=False, application=None): self.application = application self.port = port + self._timeout = 20 self._client_sock = [] self._debug = debug @@ -69,6 +70,7 @@ def start(self): """ for _ in range(MAX_SOCK_NUM): new_sock = socket.socket() + new_sock.settimeout(self._timeout) new_sock.bind((None, self.port)) new_sock.listen() self._client_sock.append(new_sock) @@ -90,6 +92,7 @@ def update_poll(self): self.finish_response(result, sock) self._client_sock.remove(sock) new_sock = socket.socket() + new_sock.settimeout(self._timeout) new_sock.bind((None, self.port)) new_sock.listen() add_sock.append(new_sock) From 27e248e6cdf2e5d097082581c06258352c236dff Mon Sep 17 00:00:00 2001 From: Patrick Van Oosterwijck Date: Mon, 11 Jan 2021 17:22:16 -0700 Subject: [PATCH 5/7] Fix trailing whitespace --- CODE_OF_CONDUCT.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md index 134d510..a62e132 100644 --- a/CODE_OF_CONDUCT.md +++ b/CODE_OF_CONDUCT.md @@ -43,7 +43,7 @@ Examples of unacceptable behavior by participants include: The goal of the standards and moderation guidelines outlined here is to build and maintain a respectful community. We ask that you don’t just aim to be -"technically unimpeachable", but rather try to be your best self. +"technically unimpeachable", but rather try to be your best self. We value many things beyond technical expertise, including collaboration and supporting others within our community. Providing a positive experience for @@ -74,9 +74,9 @@ You may report in the following ways: In any situation, you may send an email to . On the Adafruit Discord, you may send an open message from any channel -to all Community Moderators by tagging @community moderators. You may -also send an open message from any channel, or a direct message to -@kattni#1507, @tannewt#4653, @Dan Halbert#1614, @cater#2442, +to all Community Moderators by tagging @community moderators. You may +also send an open message from any channel, or a direct message to +@kattni#1507, @tannewt#4653, @Dan Halbert#1614, @cater#2442, @sommersoft#0222, @Mr. Certainly#0472 or @Andon#8175. Email and direct message reports will be kept confidential. From 288c146d3c4967c401c49946ff437c8b61ba77f7 Mon Sep 17 00:00:00 2001 From: dherrada Date: Thu, 14 Jan 2021 10:40:04 -0500 Subject: [PATCH 6/7] Ran pre-commit to hopefully resolve conflicting files --- .pre-commit-config.yaml | 19 +++++++ CODE_OF_CONDUCT.md | 6 +++ adafruit_wiznet5k/adafruit_wiznet5k.py | 51 +++++++------------ adafruit_wiznet5k/adafruit_wiznet5k_dhcp.py | 25 ++------- adafruit_wiznet5k/adafruit_wiznet5k_dns.py | 25 ++------- adafruit_wiznet5k/adafruit_wiznet5k_socket.py | 26 ++-------- 6 files changed, 56 insertions(+), 96 deletions(-) create mode 100644 .pre-commit-config.yaml diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml new file mode 100644 index 0000000..aab5f1c --- /dev/null +++ b/.pre-commit-config.yaml @@ -0,0 +1,19 @@ +# SPDX-FileCopyrightText: 2020 Diego Elio Pettenò +# +# SPDX-License-Identifier: Unlicense + +repos: +- repo: https://github.com/python/black + rev: stable + hooks: + - id: black +- repo: https://github.com/fsfe/reuse-tool + rev: latest + hooks: + - id: reuse +- repo: https://github.com/pre-commit/pre-commit-hooks + rev: v2.3.0 + hooks: + - id: check-yaml + - id: end-of-file-fixer + - id: trailing-whitespace diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md index a62e132..8a55c07 100644 --- a/CODE_OF_CONDUCT.md +++ b/CODE_OF_CONDUCT.md @@ -1,3 +1,9 @@ + + # Adafruit Community Code of Conduct ## Our Pledge diff --git a/adafruit_wiznet5k/adafruit_wiznet5k.py b/adafruit_wiznet5k/adafruit_wiznet5k.py index 16b8402..ca5ae0c 100644 --- a/adafruit_wiznet5k/adafruit_wiznet5k.py +++ b/adafruit_wiznet5k/adafruit_wiznet5k.py @@ -1,29 +1,12 @@ -# The MIT License (MIT) +# SPDX-FileCopyrightText: 2010 WIZnet +# SPDX-FileCopyrightText: 2010 Arduino LLC +# SPDX-FileCopyrightText: 2008 Bjoern Hartmann +# SPDX-FileCopyrightText: 2018 Paul Stoffregen +# SPDX-FileCopyrightText: 2020 Brent Rubell for Adafruit Industries +# SPDX-FileCopyrightText: 2021 Patrick Van Oosterwijck # -# Copyright (c) 2010 WIZnet -# Copyright (c) 2010 Arduino LLC -# Copyright (c) 2008 Bjoern Hartmann -# Copyright 2018 Paul Stoffregen -# Modified by Brent Rubell for Adafruit Industries, 2020 -# Copyright (c) 2021 Patrick Van Oosterwijck -# -# Permission is hereby granted, free of charge, to any person obtaining a copy -# of this software and associated documentation files (the "Software"), to deal -# in the Software without restriction, including without limitation the rights -# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -# copies of the Software, and to permit persons to whom the Software is -# furnished to do so, subject to the following conditions: -# -# The above copyright notice and this permission notice shall be included in -# all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -# THE SOFTWARE. +# SPDX-License-Identifier: MIT + """ `adafruit_wiznet5k` ================================================================================ @@ -770,13 +753,17 @@ def socket_write(self, socket_num, buffer, timeout=0): while ( self._read_socket(socket_num, REG_SNIR)[0] & SNIR_SEND_OK ) != SNIR_SEND_OK: - if self.socket_status(socket_num)[0] in ( - SNSR_SOCK_CLOSED, - SNSR_SOCK_TIME_WAIT, - SNSR_SOCK_FIN_WAIT, - SNSR_SOCK_CLOSE_WAIT, - SNSR_SOCK_CLOSING, - ) or (timeout and time.monotonic() - stamp > timeout): + if ( + self.socket_status(socket_num)[0] + in ( + SNSR_SOCK_CLOSED, + SNSR_SOCK_TIME_WAIT, + SNSR_SOCK_FIN_WAIT, + SNSR_SOCK_CLOSE_WAIT, + SNSR_SOCK_CLOSING, + ) + or (timeout and time.monotonic() - stamp > timeout) + ): # self.socket_close(socket_num) return 0 time.sleep(0.01) diff --git a/adafruit_wiznet5k/adafruit_wiznet5k_dhcp.py b/adafruit_wiznet5k/adafruit_wiznet5k_dhcp.py index 975111c..7faef3d 100644 --- a/adafruit_wiznet5k/adafruit_wiznet5k_dhcp.py +++ b/adafruit_wiznet5k/adafruit_wiznet5k_dhcp.py @@ -1,25 +1,8 @@ -# The MIT License (MIT) +# SPDX-FileCopyrightText: 2009 Jordan Terell (blog.jordanterrell.com) +# SPDX-FileCopyrightText: 2020 Brent Rubell for Adafruit Industries # -# Copyright (c) April 25, 2009 Jordan Terrell (blog.jordanterrell.com) -# Modified by Brent Rubell for Adafruit Industries, 2020 -# -# Permission is hereby granted, free of charge, to any person obtaining a copy -# of this software and associated documentation files (the "Software"), to deal -# in the Software without restriction, including without limitation the rights -# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -# copies of the Software, and to permit persons to whom the Software is -# furnished to do so, subject to the following conditions: -# -# The above copyright notice and this permission notice shall be included in -# all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -# THE SOFTWARE. +# SPDX-License-Identifier: MIT + """ `adafruit_wiznet5k_dhcp` ================================================================================ diff --git a/adafruit_wiznet5k/adafruit_wiznet5k_dns.py b/adafruit_wiznet5k/adafruit_wiznet5k_dns.py index d2e0375..d72b5d1 100644 --- a/adafruit_wiznet5k/adafruit_wiznet5k_dns.py +++ b/adafruit_wiznet5k/adafruit_wiznet5k_dns.py @@ -1,25 +1,8 @@ -# The MIT License (MIT) +# SPDX-FileCopyrightText: 2009-2010 MCQN Ltd +# SPDX-FileCopyrightText: Brent Rubell for Adafruit Industries # -# (c) Copyright 2009-2010 MCQN Ltd -# Modified by Brent Rubell for Adafruit Industries, 2020 -# -# Permission is hereby granted, free of charge, to any person obtaining a copy -# of this software and associated documentation files (the "Software"), to deal -# in the Software without restriction, including without limitation the rights -# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -# copies of the Software, and to permit persons to whom the Software is -# furnished to do so, subject to the following conditions: -# -# The above copyright notice and this permission notice shall be included in -# all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -# THE SOFTWARE. +# SPDX-License-Identifier: MIT + """ `adafruit_wiznet5k_dns` ================================================================================ diff --git a/adafruit_wiznet5k/adafruit_wiznet5k_socket.py b/adafruit_wiznet5k/adafruit_wiznet5k_socket.py index 2d94f56..4033659 100644 --- a/adafruit_wiznet5k/adafruit_wiznet5k_socket.py +++ b/adafruit_wiznet5k/adafruit_wiznet5k_socket.py @@ -1,26 +1,8 @@ -# The MIT License (MIT) +# SPDX-FileCopyrightText: 2019 ladyada for Adafruit Industries +# SPDX-FileCopyrightText: 2020 Brent Rubell for Adafruit Industries # -# Copyright (c) 2019 ladyada for Adafruit Industries -# Modified by Brent Rubell for Adafruit Industries, 2020 -# Copyright (c) 2020 Patrick Van Oosterwijck -# -# Permission is hereby granted, free of charge, to any person obtaining a copy -# of this software and associated documentation files (the "Software"), to deal -# in the Software without restriction, including without limitation the rights -# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -# copies of the Software, and to permit persons to whom the Software is -# furnished to do so, subject to the following conditions: -# -# The above copyright notice and this permission notice shall be included in -# all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -# THE SOFTWARE. +# SPDX-License-Identifier: MIT + """ `adafruit_wiznet5k_socket` ================================================================================ From 876a86cab2bcd52fac9df3a30285ff6e11acf7ea Mon Sep 17 00:00:00 2001 From: dherrada Date: Thu, 14 Jan 2021 10:43:21 -0500 Subject: [PATCH 7/7] Adding workflow files since Actions isn't running --- .github/workflows/build.yml | 28 ++++++++++++++++++++++++---- .github/workflows/release.yml | 4 ++++ 2 files changed, 28 insertions(+), 4 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index b6977a9..59baa53 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -1,3 +1,7 @@ +# SPDX-FileCopyrightText: 2017 Scott Shawcroft, written for Adafruit Industries +# +# SPDX-License-Identifier: MIT + name: Build CI on: [pull_request, push] @@ -38,20 +42,36 @@ jobs: # (e.g. - apt-get: gettext, etc; pip: circuitpython-build-tools, requirements.txt; etc.) run: | source actions-ci/install.sh - - name: Pip install pylint, black, & Sphinx + - name: Pip install pylint, Sphinx, pre-commit run: | - pip install --force-reinstall pylint black==19.10b0 Sphinx sphinx-rtd-theme + pip install --force-reinstall pylint Sphinx sphinx-rtd-theme pre-commit - name: Library version run: git describe --dirty --always --tags - - name: Check formatting + - name: Pre-commit hooks run: | - black --check --target-version=py35 . + pre-commit run --all-files - name: PyLint run: | pylint $( find . -path './adafruit*.py' ) ([[ ! -d "examples" ]] || pylint --disable=missing-docstring,invalid-name,bad-whitespace $( find . -path "./examples/*.py" )) - name: Build assets run: circuitpython-build-bundles --filename_prefix ${{ steps.repo-name.outputs.repo-name }} --library_location . + - name: Archive bundles + uses: actions/upload-artifact@v2 + with: + name: bundles + path: ${{ github.workspace }}/bundles/ - name: Build docs working-directory: docs run: sphinx-build -E -W -b html . _build/html + - name: Check For setup.py + id: need-pypi + run: | + echo ::set-output name=setup-py::$( find . -wholename './setup.py' ) + - name: Build Python package + if: contains(steps.need-pypi.outputs.setup-py, 'setup.py') + run: | + pip install --upgrade setuptools wheel twine readme_renderer testresources + python setup.py sdist + python setup.py bdist_wheel --universal + twine check dist/* diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 18efb9c..6d0015a 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -1,3 +1,7 @@ +# SPDX-FileCopyrightText: 2017 Scott Shawcroft, written for Adafruit Industries +# +# SPDX-License-Identifier: MIT + name: Release Actions on: