1+ import os
12import logging
23from functools import partial
4+ from sys import platform
5+ from typing import Awaitable
36
4- from trio import socket , open_nursery
7+ from trio import socket , open_nursery , move_on_after
58from oscpy .server import OSCBaseServer , UDP_MAX_SIZE
69
710logging .basicConfig ()
@@ -33,24 +36,34 @@ async def listen(
3336 "Unknown socket family, accepted values are 'unix' and 'inet'"
3437 )
3538
36- sock = await self .get_socket (family_ , (address , port ))
39+ if family == 'unix' :
40+ addr = address
41+ else :
42+ addr = (address , port )
43+ sock = await self .get_socket (family_ , addr )
3744 self .add_socket (sock , default )
3845 return sock
3946
4047 async def _listen (self , sock ):
4148 async with open_nursery () as nursery :
4249 self .nurseries [sock ] = nursery
43- while True :
44- data , addr = await sock .recvfrom (UDP_MAX_SIZE )
45- nursery .start_soon (
46- partial (
47- self .handle_message ,
48- data ,
49- addr ,
50- drop_late = False ,
51- sender_socket = sock
50+ try :
51+ while True :
52+ data , addr = await sock .recvfrom (UDP_MAX_SIZE )
53+ nursery .start_soon (
54+ partial (
55+ self .handle_message ,
56+ data ,
57+ addr ,
58+ drop_late = False ,
59+ sender_socket = sock
60+ )
5261 )
53- )
62+ finally :
63+ with move_on_after (1 ) as cleanup_scope :
64+ cleanup_scope .shield = True
65+ logger .info ("socket %s cancelled" , sock )
66+ await self .stop (sock )
5467
5568 async def handle_message (self , data , sender , drop_late , sender_socket ):
5669 for callbacks , values , address in self .callbacks (data , sender , sender_socket ):
@@ -60,13 +73,17 @@ async def _execute_callbacks(self, callbacks_list, address, values):
6073 for cb , get_address in callbacks_list :
6174 try :
6275 if get_address :
63- await cb (address , * values )
76+ result = cb (address , * values )
6477 else :
65- await cb (* values )
78+ result = cb (* values )
79+ if isinstance (result , Awaitable ):
80+ await result
81+
6682 except Exception :
6783 if self .intercept_errors :
68- logger .error ("Unhandled exception caught in oscpy server" , exc_info = True )
84+ logger .error ("Ignoring unhandled exception caught in oscpy server" , exc_info = True )
6985 else :
86+ logger .exception ("Unhandled exception caught in oscpy server" )
7087 raise
7188
7289 async def process (self ):
@@ -80,9 +97,41 @@ async def stop_all(self):
8097 """
8198 self .nursery .cancel_scope .deadline = 0
8299
83- async def stop (self , sock ):
84- nursery = self .nurseries .pop (sock )
85- nursery .cancel_scope .deadline = 0
100+ async def stop (self , sock = None ):
101+ if sock is None :
102+ if self .default_socket :
103+ sock = self .default_socket
104+ else :
105+ raise RuntimeError ('no default socket yet and no socket provided' )
106+ if sock in self .sockets :
107+ self .sockets .remove (sock )
108+ else :
109+ raise RuntimeError ("Socket %s is not managed by this server" % sock )
110+ sock .close ()
111+ if sock in self .nurseries :
112+ nursery = self .nurseries .pop (sock )
113+ nursery .cancel_scope .deadline = 0
114+
115+ if sock is self .default_socket :
116+ self .default_socket = None
117+
118+ async def close (self , sock = None ):
119+ """Close a socket opened by the server."""
120+ if not sock and self .default_socket :
121+ sock = self .default_socket
122+ elif not sock :
123+ raise RuntimeError ('no default socket yet and no socket provided' )
124+
125+ if sock not in self .sockets :
126+ logger .warning ("Ignoring requested to close an unknown socket %s" % sock )
127+
128+ if sock == self .default_socket :
129+ self .default_socket = None
130+
131+ if platform != 'win32' and sock .family == socket .AF_UNIX :
132+ os .unlink (sock .getsockname ())
133+ else :
134+ sock .close ()
86135
87136 def getaddress (self , sock = None ):
88137 """Wrap call to getsockname.
0 commit comments