1
+ import os
1
2
import logging
2
3
from functools import partial
4
+ from sys import platform
5
+ from typing import Awaitable
3
6
4
- from trio import socket , open_nursery
7
+ from trio import socket , open_nursery , move_on_after
5
8
from oscpy .server import OSCBaseServer , UDP_MAX_SIZE
6
9
7
10
logging .basicConfig ()
@@ -33,24 +36,34 @@ async def listen(
33
36
"Unknown socket family, accepted values are 'unix' and 'inet'"
34
37
)
35
38
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 )
37
44
self .add_socket (sock , default )
38
45
return sock
39
46
40
47
async def _listen (self , sock ):
41
48
async with open_nursery () as nursery :
42
49
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
+ )
52
61
)
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 )
54
67
55
68
async def handle_message (self , data , sender , drop_late , sender_socket ):
56
69
for callbacks , values , address in self .callbacks (data , sender , sender_socket ):
@@ -60,13 +73,17 @@ async def _execute_callbacks(self, callbacks_list, address, values):
60
73
for cb , get_address in callbacks_list :
61
74
try :
62
75
if get_address :
63
- await cb (address , * values )
76
+ result = cb (address , * values )
64
77
else :
65
- await cb (* values )
78
+ result = cb (* values )
79
+ if isinstance (result , Awaitable ):
80
+ await result
81
+
66
82
except Exception :
67
83
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 )
69
85
else :
86
+ logger .exception ("Unhandled exception caught in oscpy server" )
70
87
raise
71
88
72
89
async def process (self ):
@@ -80,9 +97,41 @@ async def stop_all(self):
80
97
"""
81
98
self .nursery .cancel_scope .deadline = 0
82
99
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 ()
86
135
87
136
def getaddress (self , sock = None ):
88
137
"""Wrap call to getsockname.
0 commit comments