3
3
4
4
import errno
5
5
from contextlib import contextmanager , suppress
6
- from os import PathLike , stat , unlink
6
+ from os import stat , unlink
7
+ from os .path import exists
7
8
from stat import S_ISSOCK
8
9
from typing import TYPE_CHECKING , Final , overload
9
10
18
19
19
20
from typing_extensions import Buffer
20
21
21
- from ._socket import SocketType
22
+ from ._socket import AddressFormat , SocketType
22
23
23
24
# XX TODO: this number was picked arbitrarily. We should do experiments to
24
25
# tune it. (Or make it dynamic -- one idea is to start small and increase it
@@ -358,33 +359,25 @@ class SocketListener(Listener[SocketStream]):
358
359
incoming connections as :class:`SocketStream` objects.
359
360
360
361
Args:
361
-
362
362
socket: The Trio socket object to wrap. Must have type ``SOCK_STREAM``,
363
363
and be listening.
364
364
365
- path: Used for keeping track of which path a Unix socket is bound
366
- to. If not ``None``, :meth:`aclose` will unlink this path.
367
- File must have socket mode flag set.
368
-
369
365
Note that the :class:`SocketListener` "takes ownership" of the given
370
- socket; closing the :class:`SocketListener` will also close the socket.
366
+ socket; closing the :class:`SocketListener` will also close the
367
+ socket, and if it's a Unix socket, it will also unlink the leftover
368
+ socket file that the Unix socket is bound to.
371
369
372
370
.. attribute:: socket
373
371
374
372
The Trio socket object that this stream wraps.
375
373
376
- .. attribute:: path
377
-
378
- The path to unlink in :meth:`aclose` that a Unix socket is bound to.
379
-
380
374
"""
381
375
382
- __slots__ = ("path" , " socket" )
376
+ __slots__ = ("socket" , )
383
377
384
378
def __init__ (
385
379
self ,
386
380
socket : SocketType ,
387
- path : str | bytes | PathLike [str ] | PathLike [bytes ] | None = None ,
388
381
) -> None :
389
382
if not isinstance (socket , tsocket .SocketType ):
390
383
raise TypeError ("SocketListener requires a Trio socket object" )
@@ -398,11 +391,8 @@ def __init__(
398
391
else :
399
392
if not listening :
400
393
raise ValueError ("SocketListener requires a listening socket" )
401
- if path is not None and not S_ISSOCK (stat (path ).st_mode ):
402
- raise ValueError ("Specified path must be a Unix socket file" )
403
394
404
395
self .socket = socket
405
- self .path = path
406
396
407
397
async def accept (self ) -> SocketStream :
408
398
"""Accept an incoming connection.
@@ -433,8 +423,21 @@ async def accept(self) -> SocketStream:
433
423
return SocketStream (sock )
434
424
435
425
async def aclose (self ) -> None :
436
- """Close this listener and its underlying socket."""
426
+ """Close this listener, its underlying socket, and for Unix sockets unlink the socket file."""
427
+ is_unix_socket = self .socket .family == getattr (tsocket , "AF_UNIX" , None )
428
+
429
+ path : AddressFormat | None = None
430
+ if is_unix_socket :
431
+ # If unix socket, need to get path before we close socket
432
+ # or OS errors
433
+ path = self .socket .getsockname ()
437
434
self .socket .close ()
438
- if self .path is not None :
439
- unlink (self .path )
435
+ # If unix socket, clean up socket file that gets left behind.
436
+ if (
437
+ is_unix_socket
438
+ and path is not None
439
+ and exists (path )
440
+ and S_ISSOCK (stat (path ).st_mode )
441
+ ):
442
+ unlink (path )
440
443
await trio .lowlevel .checkpoint ()
0 commit comments