@@ -30,39 +30,21 @@ def _wt(value: int) -> wintypes.DWORD:
3030 return wintypes .DWORD (value )
3131
3232
33- def _name_pipe ( kernel32 ) -> str :
33+ def _generate_pipe_path ( name : str | None ) -> str :
3434 """return next available numeric pipe name
3535
36- :return: "\\ .\pipe\###" where ### is a unique number
36+ :return: "\\ .\{name}" if name is given, else name is an auto-incremented integer
3737 """
38+
3839 global id
3940
40- notok = True
41- while notok :
42- name = rf"\\.\pipe\{ id } "
41+ if name is None :
42+ pname = id
4343 id += 1
44+ else :
45+ pname = name
4446
45- # make sure the pipe does not exist
46- h = kernel32 .CreateFileW (name , GENERIC_READ , 0 , None , OPEN_EXISTING , 0 , None )
47-
48- if h == INVALID_HANDLE_VALUE :
49- error_code = ctypes .get_last_error ()
50-
51- # ERROR_FILE_NOT_FOUND (2) means the pipe definitely does not exist
52- if error_code == 2 :
53- notok = False
54- elif error_code in (231 , 5 ):
55- # ERROR_ACCESS_DENIED (5) means it exists but your process lacks permissions
56- # ERROR_PIPE_BUSY (231) means the pipe exists but all instances are occupied
57- continue
58- else :
59- raise ctypes .WinError (error_code )
60- else :
61- # If it successfully opens (exists), close the handle
62- kernel32 .CloseHandle (h )
63- notok = False
64-
65- return name
47+ return rf"\\.\pipe\{ pname } "
6648
6749
6850def _win_error (code = None ):
@@ -72,6 +54,9 @@ def _win_error(code=None):
7254
7355
7456class NPopen :
57+ _path : str
58+ _pipe : int
59+
7560 def __init__ (
7661 self ,
7762 mode : Optional [str ] = "r" ,
@@ -88,7 +73,6 @@ def __init__(
8873
8974 self .kernel32 = ctypes .WinDLL ("kernel32" , use_last_error = True )
9075 self .stream : Union [IO , None ] = None # I/O stream of the pipe
91- self ._path = _name_pipe (self .kernel32 ) if name is None else rf"\\.\pipe\{ name } "
9276 self ._rd = any (mode and c in mode for c in "r+" )
9377 self ._wr = any (mode and c in mode for c in "wax+" )
9478
@@ -134,20 +118,40 @@ def __init__(
134118 buffer_size = _wt (0 )
135119 timeout = _wt (0 )
136120
137- # "open" named pipe
138- h = self .kernel32 .CreateNamedPipeW (
139- self ._path ,
140- access ,
141- pipe_mode ,
142- max_instances ,
143- buffer_size ,
144- buffer_size ,
145- timeout ,
146- None ,
147- )
148- if h == INVALID_HANDLE_VALUE :
149- raise _win_error ()
150- self ._pipe = h
121+ def try_open_pipe (name : str | None ) -> bool :
122+
123+ # "open" named pipe
124+ pipe_path = _generate_pipe_path (name )
125+
126+ h = self .kernel32 .CreateNamedPipeW (
127+ pipe_path ,
128+ access ,
129+ pipe_mode ,
130+ max_instances ,
131+ buffer_size ,
132+ buffer_size ,
133+ timeout ,
134+ None ,
135+ )
136+
137+ if h == INVALID_HANDLE_VALUE :
138+ error_code = ctypes .get_last_error ()
139+
140+ if name or error_code != 231 :
141+ raise ctypes .WinError (error_code )
142+ else :
143+ # ERROR_PIPE_BUSY (231) means the pipe exists but all instances are occupied
144+ return True # already used
145+
146+ self ._path = pipe_path
147+ self ._pipe = h
148+
149+ return False # success
150+
151+ while try_open_pipe (name ):
152+ # loop until success.
153+ # if name is given, runs only once
154+ ...
151155
152156 @property
153157 def path (self ):
0 commit comments