Alright ! Let’s talk about event loops in Python it might seem complicated at first, but once you get the hang of it, it’s pretty simple. Imagine sitting on your couch watching TV when someone knocks on your door. You jump up to answer the doorbell (event), and then go back to watching TV (loop). That’s essentially how an event loop works in Python it waits for something to happen (an “event”), performs some action, and then goes back to waiting for more events.
In this tutorial, we’ll be using Python’s built-in `select` module to set up a basic event loop. First, let’s import the necessary modules:
# Importing necessary modules
import select # Importing the select module to set up an event loop
import socket # Importing the socket module to establish network connections
from time import sleep # Importing the sleep function from the time module to pause the execution of the script
# Setting up the event loop
while True: # Creating a loop that will continue until a break statement is encountered
events = select.select([socket.socket()], [], [], 1) # Using the select function to wait for an event on the given socket for 1 second
if events[0]: # Checking if there is an event in the first element of the events list
print("Event occurred!") # Printing a message to indicate that an event has occurred
else:
print("No event occurred.") # Printing a message to indicate that no event has occurred
sleep(1) # Pausing the execution of the script for 1 second before looping again
Next, we create two sockets one for listening (our “doorbell”) and one for sending/receiving data (the actual event). Here’s what that looks like:
# Creating a socket for listening and sending/receiving data
server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) # Creates a socket object using the AF_INET address family and SOCK_STREAM protocol
server_socket.bind(('127.0.0.1', 5000)) # Binds the socket to the specified IP address and port number
server_socket.listen() # Listens for incoming connections on the bound socket
# Accepting a connection from a client
client_socket, address = server_socket.accept() # Accepts a connection from a client and returns a socket object and the client's address
Now we’re ready to set up our event loop using `select`. We create a list of file descriptors (in this case, just the listening socket and the client socket) that we want to monitor for events:
# Setting up event loop using select
# Create a list of file descriptors to monitor for events
# In this case, we only have the listening socket and client socket
# List of readable file descriptors
readable = [server_socket.fileno(), client_socket.fileno()]
# List of writable file descriptors
writable = []
# List of exceptable file descriptors
exceptable = []
We then enter our event loop using a `while` statement, which will continue running until the user presses Ctrl+C or some other external signal is received:
# Import the necessary modules
import select
import socket
# Create a server socket and bind it to a specific port
server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server_socket.bind(("localhost", 8000))
# Listen for incoming connections
server_socket.listen()
# Create lists to store file descriptors for readable, writable, and exceptable sockets
readable = [server_socket.fileno()]
writable = []
exceptable = []
# Enter the event loop
while True:
# Wait for events to occur on any of the file descriptors in readable.
# select.select() takes three lists as arguments and returns three lists
# containing the file descriptors that are ready for reading, writing, or have an error.
read_list, write_list, err_list = select.select(readable, writable, exceptable)
# Check if there are any sockets ready for reading
if len(read_list) > 0:
# Loop through the sockets that are ready for reading
for sock in read_list:
# Check if the socket is the server socket
if sock == server_socket.fileno():
# Accept the new connection and add its file descriptor to our list of readable/writable sockets.
client_sock, address = server_socket.accept()
print(f"New connection from {address}")
# Add the client socket's file descriptor to the readable list
readable.append(client_sock.fileno())
else:
# Receive data from the client socket
message = client_sock.recv(1024)
# Check if the client has disconnected
if not message:
print("Client disconnected")
# Remove the client socket's file descriptor from the readable list
readable.remove(client_sock.fileno())
# Close the client socket
client_sock.close()
else:
# Do something with the received data (e.g., send a response)...
# Add the client socket's file descriptor to the writable list
writable.append(client_sock.fileno())
# Check if there are any sockets ready for writing
if len(write_list) > 0:
# Loop through the sockets that are ready for writing
for sock in write_list:
# Create a message to send to the client
message = b"Hello, world!\n"
# Send the message to the client
client_sock.sendall(message)
# Remove the client socket's file descriptor from the writable list
writable.remove(client_sock.fileno())
# Check if there are any sockets with errors
if len(err_list) > 0:
print("Error occurred")
And that’s it! You now have a basic event loop set up using Python’s `select` module. Of course, this is just the tip of the iceberg there are many other ways to implement an event loop in Python (e.g., using asyncio or Twisted), but hopefully this gives you a good starting point for your own projects.