feat: Semaine 9

This commit is contained in:
gauvainboiche
2026-05-15 16:24:56 +02:00
parent 3315cb2336
commit ce1f0e513a
108 changed files with 3150 additions and 0 deletions
+1
View File
@@ -0,0 +1 @@
3.12
+3
View File
@@ -0,0 +1,3 @@
```
uv add websockets
```
+30
View File
@@ -0,0 +1,30 @@
import socket
from env_var import SERVER_PORT, CLIENT_PORT
def main():
client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
client.connect(("localhost", SERVER_PORT))
# client.bind(("localhost", CLIENT_PORT))
print("🤝 Clavardage démarré. Tapez vos messages à la suite. Tapez 'Q' pour quitter.")
while True:
# Clavardage
message = input("MESSAGE > ")
if message.upper() == "Q":
client.close()
break
# elif len(message) <= 0:
# return f"Rentrez un message non vide."
client.sendall(message.encode("utf-8"))
data = client.recv(1024)
response = data.decode("utf-8")
print(f"REPONSE SERVEUR : {response}")
if __name__ == "__main__":
main()
+36
View File
@@ -0,0 +1,36 @@
import socket
import threading
from env_var import SERVER_PORT, CLIENT_PORT
from utils import receive_messages, send_messages
client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
client.connect(("localhost", SERVER_PORT))
client_username = input("Rentrez votre pseudonyme (min 3 et max 12 caractères) pour la session > ")
if len(client_username) < 3 \
or len(client_username) > 12:
print(f"{client_username} contient {len(client_username)} caractères et n'est donc pas valide. Recommencez.")
quit(1)
print(f"Bienvenue, {client_username}.")
print("🤝 Clavardage démarré. Tapez vos messages à la suite. Tapez 'QUIT' ou 'Q' pour quitter.")
print()
client.sendall(f"USERNAME >>> {client_username}".encode("utf-8"))
thread_recv = threading.Thread(target=receive_messages, args=(client,))
thread_send = threading.Thread(target=send_messages, args=(client, client_username))
thread_recv.daemon = False
thread_send.daemon = True
thread_recv.start()
thread_send.start()
thread_recv.join()
thread_send.join()
client.close()
print("❌ Connexion fermée.")
@@ -0,0 +1,24 @@
#client
import socket
import threading
from utils import receive_messages, send_messages
client_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
client_socket.connect(("localhost", 9999))
pseudo = input("Choisissez votre pseudo : ")
client_socket.send(pseudo.encode())
print(f"Connecté au chat en tant que {pseudo} !\n")
thread_recv = threading.Thread(target=receive_messages, args=(client_socket,))
thread_send = threading.Thread(target=send_messages, args=(client_socket,))
thread_recv.start()
thread_send.start()
thread_recv.join()
thread_send.join()
print("Chat terminé.")
@@ -0,0 +1,23 @@
# client
import asyncio
import websockets
async def recevoir(websocket):
async for message in websocket:
print(f"\n[Message reçu] {message}")
async def envoyer(websocket):
while True:
message = await asyncio.get_event_loop().run_in_executor(None, input, "Vous : ")
await websocket.send(message)
async def main():
uri = "ws://localhost:8765"
async with websockets.connect(uri) as websocket:
print("Connecté au chat. Tapez vos messages.")
await asyncio.gather(
recevoir(websocket),
envoyer(websocket),
)
asyncio.run(main())
+2
View File
@@ -0,0 +1,2 @@
SERVER_PORT = 9001
CLIENT_PORT = 9002
+16
View File
@@ -0,0 +1,16 @@
## Exercice : chat multi-utilisateurs avec pseudo
### Consigne
Créez un système de chat qui permet à **plusieurs clients** de se connecter en même temps à un serveur. Chaque client choisit un pseudo à la connexion, et tous les messages sont diffusés à l'ensemble des participants avec le pseudo de l'expéditeur.
**Fonctionnalités attendues :**
1. Le serveur accepte plusieurs connexions simultanées (un thread par client)
2. À la connexion, le client envoie son pseudo en premier message
3. Le serveur annonce l'arrivée du nouveau participant à tous les autres
4. Quand un client envoie un message, le serveur le redistribue à tous les autres clients (pas à l'expéditeur)
5. Quand un client se déconnecte (ou tape `quit`), le serveur prévient les autres
6. Le client utilise deux threads : un pour envoyer, un pour recevoir (comme dans la version full-duplex)
**Exemple d'échange vu par un client :**
+6
View File
@@ -0,0 +1,6 @@
def main():
print("Hello from socket-chat!")
if __name__ == "__main__":
main()
+9
View File
@@ -0,0 +1,9 @@
[project]
name = "socket-chat"
version = "0.1.0"
description = "Add your description here"
readme = "README.md"
requires-python = ">=3.12"
dependencies = [
"websockets>=16.0",
]
+48
View File
@@ -0,0 +1,48 @@
import socket, threading
from env_var import SERVER_PORT, CLIENT_PORT
def handle_connection(client):
while True:
data = client.recv(1024)
if not data:
break
message = data.decode("utf-8")
print(f"MESSAGE RECU : {message}")
response = f"MESSAGE RECU PAR LE SERVEUR : {message}"
client.sendall(response.encode("utf-8"))
client.close()
def main():
server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
server.bind(("localhost", SERVER_PORT))
server.listen(5)
print(f"🤝 Clavardage démarré sur port {SERVER_PORT}. En attente de client.")
while True:
client, client_address = server.accept()
client_thread_receive = threading.Thread(
target= handle_connection,
args=(client,)
)
client_thread_sending = threading.Thread(
target= handle_connection,
args=(client,)
)
client_thread_receive.start()
client_thread_sending.start()
client_thread_receive.join()
client_thread_sending.join()
print(f"Client connecté : {client_address}")
print(f"Serveur de clavardage prêt.")
print()
if __name__ == "__main__":
main()
+44
View File
@@ -0,0 +1,44 @@
import socket
import threading
from env_var import SERVER_PORT, CLIENT_PORT
from utils import receive_messages, send_messages
server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
server.bind(("localhost", SERVER_PORT))
server.listen(5)
print("👥 Chat démarré, en attente de client...")
client, client_address = server.accept()
print(f"Client connecté : {client_address}")
try:
username_msg = client.recv(1024).decode("utf-8")
if username_msg.startswith("USERNAME:"):
client_username = username_msg.split(":", 1)[1]
print(f"{client_username} a rejoint le salon.")
else:
client_username = "Anonyme"
except:
client_username = "Anonyme"
print(f"⚠️ Impossible de recevoir le pseudo du client.")
print(f"🤝 Clavardage démarré sur port {SERVER_PORT}.")
thread_recv = threading.Thread(target=receive_messages, args=(client,))
thread_send = threading.Thread(target=send_messages, args=(client, "[SERVEUR]"))
thread_recv.daemon = False
thread_send.daemon = True
thread_recv.start()
thread_send.start()
thread_recv.join()
thread_send.join()
client.close()
server.close()
print(f"{client_username} s'est déconnecté. Serveur fermé.")
@@ -0,0 +1,54 @@
# server
import socket
import threading
clients = []
lock = threading.Lock()
def broadcast(message, sender_socket=None):
with lock:
for client_socket, pseudo in clients:
if client_socket != sender_socket:
try:
client_socket.send(message.encode())
except:
pass
def handle_client(client_socket, client_address):
pseudo = client_socket.recv(4096).decode().strip()
with lock:
clients.append((client_socket, pseudo))
print(f"{pseudo} ({client_address}) a rejoint le chat.")
broadcast(f"[{pseudo} a rejoint le chat]", sender_socket=client_socket)
while True:
try:
message = client_socket.recv(4096).decode()
if not message or message.lower() == "quit":
break
print(f"{pseudo} : {message}")
broadcast(f"{pseudo} : {message}", sender_socket=client_socket)
except:
break
with lock:
clients.remove((client_socket, pseudo))
client_socket.close()
print(f"{pseudo} a quitté le chat.")
broadcast(f"[{pseudo} a quitté le chat]")
server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
server_socket.bind(("localhost", 9999))
server_socket.listen(5)
print("Serveur de chat multi-utilisateurs démarré sur le port 9999...")
while True:
client_socket, client_address = server_socket.accept()
thread = threading.Thread(target=handle_client, args=(client_socket, client_address))
thread.start()
@@ -0,0 +1,28 @@
# server
import asyncio
import websockets
clients_connectes : set[websockets.ServerConnection] = set()
async def handle_client(websocket: websockets.ServerConnection):
clients_connectes.add(websocket)
print(f"Connexion : {websocket.remote_address}{len(clients_connectes)} client(s) connecté(s)")
try:
async for message in websocket:
print(f"Message de {websocket.remote_address} : {message}")
destinataires = clients_connectes - {websocket}
if destinataires:
await asyncio.gather(
*[client.send(message) for client in destinataires]
)
finally:
clients_connectes.discard(websocket)
print(f"Déconnexion : {websocket.remote_address}{len(clients_connectes)} client(s) restant(s)")
async def main():
async with websockets.serve(handle_client, "localhost", 8765):
print("Serveur de chat sur ws://localhost:8765")
await asyncio.Future()
asyncio.run(main())
+32
View File
@@ -0,0 +1,32 @@
import socket
def receive_messages(sock: socket.socket):
while True:
try:
message = sock.recv(4096).decode()
if not message:
print("\nConnexion fermée.")
sock.close()
break
if message.startswith("QUIT:"):
print(f"\n{message.split(':', 1)[1]} s'est déconnecté.")
sock.close()
break
print(f"\n{message}")
print("Vous > ", end="", flush=True)
except:
break
def send_messages(sock: socket.socket, username: str):
while True:
try:
message = input("Vous > ")
if message.upper() == 'Q' \
or message.upper() == 'QUIT':
sock.sendall(f"QUIT:{username}".encode())
break
if message:
complete_message = f"{username} > {message}"
sock.sendall(complete_message.encode())
except:
break
+59
View File
@@ -0,0 +1,59 @@
version = 1
revision = 3
requires-python = ">=3.12"
[[package]]
name = "socket-chat"
version = "0.1.0"
source = { virtual = "." }
dependencies = [
{ name = "websockets" },
]
[package.metadata]
requires-dist = [{ name = "websockets", specifier = ">=16.0" }]
[[package]]
name = "websockets"
version = "16.0"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/04/24/4b2031d72e840ce4c1ccb255f693b15c334757fc50023e4db9537080b8c4/websockets-16.0.tar.gz", hash = "sha256:5f6261a5e56e8d5c42a4497b364ea24d94d9563e8fbd44e78ac40879c60179b5", size = 179346, upload-time = "2026-01-10T09:23:47.181Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/84/7b/bac442e6b96c9d25092695578dda82403c77936104b5682307bd4deb1ad4/websockets-16.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:71c989cbf3254fbd5e84d3bff31e4da39c43f884e64f2551d14bb3c186230f00", size = 177365, upload-time = "2026-01-10T09:22:46.787Z" },
{ url = "https://files.pythonhosted.org/packages/b0/fe/136ccece61bd690d9c1f715baaeefd953bb2360134de73519d5df19d29ca/websockets-16.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:8b6e209ffee39ff1b6d0fa7bfef6de950c60dfb91b8fcead17da4ee539121a79", size = 175038, upload-time = "2026-01-10T09:22:47.999Z" },
{ url = "https://files.pythonhosted.org/packages/40/1e/9771421ac2286eaab95b8575b0cb701ae3663abf8b5e1f64f1fd90d0a673/websockets-16.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:86890e837d61574c92a97496d590968b23c2ef0aeb8a9bc9421d174cd378ae39", size = 175328, upload-time = "2026-01-10T09:22:49.809Z" },
{ url = "https://files.pythonhosted.org/packages/18/29/71729b4671f21e1eaa5d6573031ab810ad2936c8175f03f97f3ff164c802/websockets-16.0-cp312-cp312-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:9b5aca38b67492ef518a8ab76851862488a478602229112c4b0d58d63a7a4d5c", size = 184915, upload-time = "2026-01-10T09:22:51.071Z" },
{ url = "https://files.pythonhosted.org/packages/97/bb/21c36b7dbbafc85d2d480cd65df02a1dc93bf76d97147605a8e27ff9409d/websockets-16.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e0334872c0a37b606418ac52f6ab9cfd17317ac26365f7f65e203e2d0d0d359f", size = 186152, upload-time = "2026-01-10T09:22:52.224Z" },
{ url = "https://files.pythonhosted.org/packages/4a/34/9bf8df0c0cf88fa7bfe36678dc7b02970c9a7d5e065a3099292db87b1be2/websockets-16.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:a0b31e0b424cc6b5a04b8838bbaec1688834b2383256688cf47eb97412531da1", size = 185583, upload-time = "2026-01-10T09:22:53.443Z" },
{ url = "https://files.pythonhosted.org/packages/47/88/4dd516068e1a3d6ab3c7c183288404cd424a9a02d585efbac226cb61ff2d/websockets-16.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:485c49116d0af10ac698623c513c1cc01c9446c058a4e61e3bf6c19dff7335a2", size = 184880, upload-time = "2026-01-10T09:22:55.033Z" },
{ url = "https://files.pythonhosted.org/packages/91/d6/7d4553ad4bf1c0421e1ebd4b18de5d9098383b5caa1d937b63df8d04b565/websockets-16.0-cp312-cp312-win32.whl", hash = "sha256:eaded469f5e5b7294e2bdca0ab06becb6756ea86894a47806456089298813c89", size = 178261, upload-time = "2026-01-10T09:22:56.251Z" },
{ url = "https://files.pythonhosted.org/packages/c3/f0/f3a17365441ed1c27f850a80b2bc680a0fa9505d733fe152fdf5e98c1c0b/websockets-16.0-cp312-cp312-win_amd64.whl", hash = "sha256:5569417dc80977fc8c2d43a86f78e0a5a22fee17565d78621b6bb264a115d4ea", size = 178693, upload-time = "2026-01-10T09:22:57.478Z" },
{ url = "https://files.pythonhosted.org/packages/cc/9c/baa8456050d1c1b08dd0ec7346026668cbc6f145ab4e314d707bb845bf0d/websockets-16.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:878b336ac47938b474c8f982ac2f7266a540adc3fa4ad74ae96fea9823a02cc9", size = 177364, upload-time = "2026-01-10T09:22:59.333Z" },
{ url = "https://files.pythonhosted.org/packages/7e/0c/8811fc53e9bcff68fe7de2bcbe75116a8d959ac699a3200f4847a8925210/websockets-16.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:52a0fec0e6c8d9a784c2c78276a48a2bdf099e4ccc2a4cad53b27718dbfd0230", size = 175039, upload-time = "2026-01-10T09:23:01.171Z" },
{ url = "https://files.pythonhosted.org/packages/aa/82/39a5f910cb99ec0b59e482971238c845af9220d3ab9fa76dd9162cda9d62/websockets-16.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:e6578ed5b6981005df1860a56e3617f14a6c307e6a71b4fff8c48fdc50f3ed2c", size = 175323, upload-time = "2026-01-10T09:23:02.341Z" },
{ url = "https://files.pythonhosted.org/packages/bd/28/0a25ee5342eb5d5f297d992a77e56892ecb65e7854c7898fb7d35e9b33bd/websockets-16.0-cp313-cp313-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:95724e638f0f9c350bb1c2b0a7ad0e83d9cc0c9259f3ea94e40d7b02a2179ae5", size = 184975, upload-time = "2026-01-10T09:23:03.756Z" },
{ url = "https://files.pythonhosted.org/packages/f9/66/27ea52741752f5107c2e41fda05e8395a682a1e11c4e592a809a90c6a506/websockets-16.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c0204dc62a89dc9d50d682412c10b3542d748260d743500a85c13cd1ee4bde82", size = 186203, upload-time = "2026-01-10T09:23:05.01Z" },
{ url = "https://files.pythonhosted.org/packages/37/e5/8e32857371406a757816a2b471939d51c463509be73fa538216ea52b792a/websockets-16.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:52ac480f44d32970d66763115edea932f1c5b1312de36df06d6b219f6741eed8", size = 185653, upload-time = "2026-01-10T09:23:06.301Z" },
{ url = "https://files.pythonhosted.org/packages/9b/67/f926bac29882894669368dc73f4da900fcdf47955d0a0185d60103df5737/websockets-16.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:6e5a82b677f8f6f59e8dfc34ec06ca6b5b48bc4fcda346acd093694cc2c24d8f", size = 184920, upload-time = "2026-01-10T09:23:07.492Z" },
{ url = "https://files.pythonhosted.org/packages/3c/a1/3d6ccdcd125b0a42a311bcd15a7f705d688f73b2a22d8cf1c0875d35d34a/websockets-16.0-cp313-cp313-win32.whl", hash = "sha256:abf050a199613f64c886ea10f38b47770a65154dc37181bfaff70c160f45315a", size = 178255, upload-time = "2026-01-10T09:23:09.245Z" },
{ url = "https://files.pythonhosted.org/packages/6b/ae/90366304d7c2ce80f9b826096a9e9048b4bb760e44d3b873bb272cba696b/websockets-16.0-cp313-cp313-win_amd64.whl", hash = "sha256:3425ac5cf448801335d6fdc7ae1eb22072055417a96cc6b31b3861f455fbc156", size = 178689, upload-time = "2026-01-10T09:23:10.483Z" },
{ url = "https://files.pythonhosted.org/packages/f3/1d/e88022630271f5bd349ed82417136281931e558d628dd52c4d8621b4a0b2/websockets-16.0-cp314-cp314-macosx_10_15_universal2.whl", hash = "sha256:8cc451a50f2aee53042ac52d2d053d08bf89bcb31ae799cb4487587661c038a0", size = 177406, upload-time = "2026-01-10T09:23:12.178Z" },
{ url = "https://files.pythonhosted.org/packages/f2/78/e63be1bf0724eeb4616efb1ae1c9044f7c3953b7957799abb5915bffd38e/websockets-16.0-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:daa3b6ff70a9241cf6c7fc9e949d41232d9d7d26fd3522b1ad2b4d62487e9904", size = 175085, upload-time = "2026-01-10T09:23:13.511Z" },
{ url = "https://files.pythonhosted.org/packages/bb/f4/d3c9220d818ee955ae390cf319a7c7a467beceb24f05ee7aaaa2414345ba/websockets-16.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:fd3cb4adb94a2a6e2b7c0d8d05cb94e6f1c81a0cf9dc2694fb65c7e8d94c42e4", size = 175328, upload-time = "2026-01-10T09:23:14.727Z" },
{ url = "https://files.pythonhosted.org/packages/63/bc/d3e208028de777087e6fb2b122051a6ff7bbcca0d6df9d9c2bf1dd869ae9/websockets-16.0-cp314-cp314-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:781caf5e8eee67f663126490c2f96f40906594cb86b408a703630f95550a8c3e", size = 185044, upload-time = "2026-01-10T09:23:15.939Z" },
{ url = "https://files.pythonhosted.org/packages/ad/6e/9a0927ac24bd33a0a9af834d89e0abc7cfd8e13bed17a86407a66773cc0e/websockets-16.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:caab51a72c51973ca21fa8a18bd8165e1a0183f1ac7066a182ff27107b71e1a4", size = 186279, upload-time = "2026-01-10T09:23:17.148Z" },
{ url = "https://files.pythonhosted.org/packages/b9/ca/bf1c68440d7a868180e11be653c85959502efd3a709323230314fda6e0b3/websockets-16.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:19c4dc84098e523fd63711e563077d39e90ec6702aff4b5d9e344a60cb3c0cb1", size = 185711, upload-time = "2026-01-10T09:23:18.372Z" },
{ url = "https://files.pythonhosted.org/packages/c4/f8/fdc34643a989561f217bb477cbc47a3a07212cbda91c0e4389c43c296ebf/websockets-16.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:a5e18a238a2b2249c9a9235466b90e96ae4795672598a58772dd806edc7ac6d3", size = 184982, upload-time = "2026-01-10T09:23:19.652Z" },
{ url = "https://files.pythonhosted.org/packages/dd/d1/574fa27e233764dbac9c52730d63fcf2823b16f0856b3329fc6268d6ae4f/websockets-16.0-cp314-cp314-win32.whl", hash = "sha256:a069d734c4a043182729edd3e9f247c3b2a4035415a9172fd0f1b71658a320a8", size = 177915, upload-time = "2026-01-10T09:23:21.458Z" },
{ url = "https://files.pythonhosted.org/packages/8a/f1/ae6b937bf3126b5134ce1f482365fde31a357c784ac51852978768b5eff4/websockets-16.0-cp314-cp314-win_amd64.whl", hash = "sha256:c0ee0e63f23914732c6d7e0cce24915c48f3f1512ec1d079ed01fc629dab269d", size = 178381, upload-time = "2026-01-10T09:23:22.715Z" },
{ url = "https://files.pythonhosted.org/packages/06/9b/f791d1db48403e1f0a27577a6beb37afae94254a8c6f08be4a23e4930bc0/websockets-16.0-cp314-cp314t-macosx_10_15_universal2.whl", hash = "sha256:a35539cacc3febb22b8f4d4a99cc79b104226a756aa7400adc722e83b0d03244", size = 177737, upload-time = "2026-01-10T09:23:24.523Z" },
{ url = "https://files.pythonhosted.org/packages/bd/40/53ad02341fa33b3ce489023f635367a4ac98b73570102ad2cdd770dacc9a/websockets-16.0-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:b784ca5de850f4ce93ec85d3269d24d4c82f22b7212023c974c401d4980ebc5e", size = 175268, upload-time = "2026-01-10T09:23:25.781Z" },
{ url = "https://files.pythonhosted.org/packages/74/9b/6158d4e459b984f949dcbbb0c5d270154c7618e11c01029b9bbd1bb4c4f9/websockets-16.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:569d01a4e7fba956c5ae4fc988f0d4e187900f5497ce46339c996dbf24f17641", size = 175486, upload-time = "2026-01-10T09:23:27.033Z" },
{ url = "https://files.pythonhosted.org/packages/e5/2d/7583b30208b639c8090206f95073646c2c9ffd66f44df967981a64f849ad/websockets-16.0-cp314-cp314t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:50f23cdd8343b984957e4077839841146f67a3d31ab0d00e6b824e74c5b2f6e8", size = 185331, upload-time = "2026-01-10T09:23:28.259Z" },
{ url = "https://files.pythonhosted.org/packages/45/b0/cce3784eb519b7b5ad680d14b9673a31ab8dcb7aad8b64d81709d2430aa8/websockets-16.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:152284a83a00c59b759697b7f9e9cddf4e3c7861dd0d964b472b70f78f89e80e", size = 186501, upload-time = "2026-01-10T09:23:29.449Z" },
{ url = "https://files.pythonhosted.org/packages/19/60/b8ebe4c7e89fb5f6cdf080623c9d92789a53636950f7abacfc33fe2b3135/websockets-16.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:bc59589ab64b0022385f429b94697348a6a234e8ce22544e3681b2e9331b5944", size = 186062, upload-time = "2026-01-10T09:23:31.368Z" },
{ url = "https://files.pythonhosted.org/packages/88/a8/a080593f89b0138b6cba1b28f8df5673b5506f72879322288b031337c0b8/websockets-16.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:32da954ffa2814258030e5a57bc73a3635463238e797c7375dc8091327434206", size = 185356, upload-time = "2026-01-10T09:23:32.627Z" },
{ url = "https://files.pythonhosted.org/packages/c2/b6/b9afed2afadddaf5ebb2afa801abf4b0868f42f8539bfe4b071b5266c9fe/websockets-16.0-cp314-cp314t-win32.whl", hash = "sha256:5a4b4cc550cb665dd8a47f868c8d04c8230f857363ad3c9caf7a0c3bf8c61ca6", size = 178085, upload-time = "2026-01-10T09:23:33.816Z" },
{ url = "https://files.pythonhosted.org/packages/9f/3e/28135a24e384493fa804216b79a6a6759a38cc4ff59118787b9fb693df93/websockets-16.0-cp314-cp314t-win_amd64.whl", hash = "sha256:b14dc141ed6d2dde437cddb216004bcac6a1df0935d79656387bd41632ba0bbd", size = 178531, upload-time = "2026-01-10T09:23:35.016Z" },
{ url = "https://files.pythonhosted.org/packages/6f/28/258ebab549c2bf3e64d2b0217b973467394a9cea8c42f70418ca2c5d0d2e/websockets-16.0-py3-none-any.whl", hash = "sha256:1637db62fad1dc833276dded54215f2c7fa46912301a24bd94d45d46a011ceec", size = 171598, upload-time = "2026-01-10T09:23:45.395Z" },
]