1- import win32pipe , win32file , win32api , winerror , win32con , pywintypes
1+ import ctypes
2+ from ctypes import wintypes
23from os import path
34import io
45from typing import TypeVar , NewType , Literal , IO , Optional , Union
56
67WritableBuffer = TypeVar ("WritableBuffer" )
78PyHANDLE = NewType ("PyHANDLE" , int )
89
10+ # Define global constants
11+ PIPE_ACCESS_DUPLEX = 0x00000003
12+ PIPE_ACCESS_INBOUND = 0x00000001
13+ PIPE_ACCESS_OUTBOUND = 0x00000002
14+ PIPE_TYPE_BYTE = 0x00000000
15+ PIPE_READMODE_BYTE = 0x00000000
16+ PIPE_WAIT = 0x00000000
17+ PIPE_UNLIMITED_INSTANCES = 0xFFFFFFFF
18+ ERROR_PIPE_CONNECTED = 535
19+ ERROR_BROKEN_PIPE = 109
20+ ERROR_MORE_DATA = 234
21+ ERROR_IO_PENDING = 997
22+ FORMAT_MESSAGE_FROM_SYSTEM = 0x00001000
23+ INVALID_HANDLE_VALUE = wintypes .HANDLE (- 1 ).value
24+
925id = 0
1026
27+ def _wt (value : int ) -> wintypes .DWORD :
28+ return wintypes .DWORD (value )
1129
1230def _name_pipe ():
1331 global id
@@ -23,11 +41,18 @@ def _name_pipe():
2341
2442def _win_error (code = None ):
2543 if not code :
26- code = win32api .GetLastError ()
27- return OSError (
28- f"[OS Error { code } ] { win32api .FormatMessage (win32con .FORMAT_MESSAGE_FROM_SYSTEM ,0 ,code ,0 ,None )} "
44+ code = ctypes .GetLastError ()
45+ buf = ctypes .create_unicode_buffer (256 )
46+ ctypes .FormatMessageW (
47+ FORMAT_MESSAGE_FROM_SYSTEM ,
48+ None ,
49+ code ,
50+ 0 ,
51+ buf ,
52+ len (buf ),
53+ None
2954 )
30-
55+ return OSError ( f"[OS Error { code } ] { buf . value } " )
3156
3257class NPopen :
3358 def __init__ (
@@ -72,30 +97,26 @@ def __init__(
7297 )
7398
7499 self ._bufsize = - 1 if txt_mode and bufsize == 1 else bufsize
75-
76100 if self ._rd and self ._wr :
77- access = win32pipe . PIPE_ACCESS_DUPLEX
101+ access = _wt ( PIPE_ACCESS_DUPLEX )
78102 elif self ._rd :
79- access = win32pipe . PIPE_ACCESS_INBOUND
103+ access = _wt ( PIPE_ACCESS_INBOUND )
80104 elif self ._wr :
81- access = win32pipe . PIPE_ACCESS_OUTBOUND
105+ access = _wt ( PIPE_ACCESS_OUTBOUND )
82106 else :
83107 raise ValueError ("Invalid mode" )
84108 # TODO: assess options: FILE_FLAG_WRITE_THROUGH, FILE_FLAG_OVERLAPPED, FILE_FLAG_FIRST_PIPE_INSTANCE
109+ pipe_mode = _wt (PIPE_TYPE_BYTE | PIPE_READMODE_BYTE | PIPE_WAIT )
85110
86- pipe_mode = (
87- win32pipe .PIPE_TYPE_BYTE
88- | win32pipe .PIPE_READMODE_BYTE
89- | win32pipe .PIPE_WAIT
90- )
91111 # TODO: assess options: PIPE_WAIT, PIPE_NOWAIT, PIPE_ACCEPT_REMOTE_CLIENTS, PIPE_REJECT_REMOTE_CLIENTS
92112
93- max_instances = win32pipe .PIPE_UNLIMITED_INSTANCES # 1
94- buffer_size = 0
95- timeout = 0
113+ max_instances = _wt (PIPE_UNLIMITED_INSTANCES )
114+ buffer_size = _wt (0 )
115+ timeout = _wt (0 )
116+
96117
97118 # "open" named pipe
98- self ._pipe = win32pipe . CreateNamedPipe (
119+ self ._pipe = ctypes . windll . kernel32 . CreateNamedPipeW (
99120 self ._path ,
100121 access ,
101122 pipe_mode ,
@@ -105,7 +126,7 @@ def __init__(
105126 timeout ,
106127 None ,
107128 )
108- if self ._pipe == win32file . INVALID_HANDLE_VALUE :
129+ if self ._pipe == INVALID_HANDLE_VALUE :
109130 raise _win_error ()
110131
111132 @property
@@ -114,28 +135,28 @@ def path(self):
114135 return self ._path
115136
116137 def __str__ (self ):
117- # return the path
118138 return self ._path
119139
120140 def close (self ):
121- # close named pipe
141+ """Close the named pipe.
142+ A closed pipe cannot be used for further I/O operations. `close()` may
143+ be called more than once without error.
144+ """
122145 if self .stream is not None :
123146 self .stream .close ()
124147 self .stream = None
125148 if self ._pipe is not None :
126- if win32file .CloseHandle (self ._pipe ):
149+ if not ctypes . windll . kernel32 .CloseHandle (self ._pipe ):
127150 raise _win_error ()
128151 self ._pipe = None
129152
130153 def wait (self ):
131-
154+ """Wait for the pipe to open (the other end to be opened) and return file object to read/write."""
132155 if not self ._pipe :
133156 raise RuntimeError ("pipe has already been closed." )
134-
135- # wait for the pipe to open (the other end to be opened) and return fileobj to read/write
136- if win32pipe .ConnectNamedPipe (self ._pipe , None ):
137- code = win32api .GetLastError ()
138- if code != 535 : # ERROR_PIPE_CONNECTED (ok, just indicating that the client has already connected)(Issue#3)
157+ if not ctypes .windll .kernel32 .ConnectNamedPipe (self ._pipe , None ):
158+ code = ctypes .GetLastError ()
159+ if code != ERROR_PIPE_CONNECTED : # (ok, just indicating that the client has already connected)(Issue#3)
139160 raise _win_error (code )
140161
141162 # create new io stream object
@@ -179,15 +200,15 @@ def writable(self) -> bool:
179200class Win32RawIO (io .RawIOBase ):
180201 """Raw I/O stream layer over open Windows pipe handle.
181202
182- `handle` is an open ``pywintypes.PyHANDLE `` object (from ``pywin32 `` package) to
203+ `handle` is an open Windows ``HANDLE `` object (from ``ctype `` package) to
183204 be wrapped by this class.
184205
185206 Specify the read and write modes by boolean flags: ``rd`` and ``wr``.
186207 """
187208
188209 def __init__ (self , handle : PyHANDLE , rd : bool , wr : bool ) -> None :
189210 super ().__init__ ()
190- self .handle = handle # PyHANDLE: Underlying Windows handle.
211+ self .handle = handle # Underlying Windows handle.
191212 self ._readable : bool = rd
192213 self ._writable : bool = wr
193214
@@ -211,7 +232,7 @@ def close(self) -> None:
211232 if self .closed :
212233 return
213234 if self .handle is not None :
214- win32file .CloseHandle (self .handle )
235+ ctypes . windll . kernel32 .CloseHandle (self .handle )
215236 self .handle = None
216237
217238 super ().close ()
@@ -224,23 +245,16 @@ def readinto(self, b: WritableBuffer) -> Union[int, None]:
224245 assert self ._readable
225246
226247 size = len (b )
227- nread = 0
228- while nread < size :
229- try :
230- hr , res = win32file .ReadFile (self .handle , size - nread )
231- if hr in (winerror .ERROR_MORE_DATA , winerror .ERROR_IO_PENDING ):
232- raise _win_error (hr )
233- except pywintypes .error as e :
234- if e .args [0 ] == 109 : # broken pipe error
235- break
236- else :
237- raise e
238- if not len (res ):
239- break
240- nnext = nread + len (res )
241- b [nread :nnext ] = res
242- nread = nnext
243- return nread
248+ nread = _wt (0 )
249+ buf = (ctypes .c_char * size ).from_buffer (b )
250+
251+ success = ctypes .windll .kernel32 .ReadFile (self .handle , buf , size , ctypes .byref (nread ), None )
252+ if not success :
253+ code = ctypes .GetLastError ()
254+ if code not in (ERROR_MORE_DATA , ERROR_IO_PENDING ):
255+ raise _win_error (code )
256+
257+ return nread .value
244258
245259 def write (self , b ):
246260 """Write buffer ``b`` to file, return number of bytes written.
@@ -250,7 +264,11 @@ def write(self, b):
250264
251265 assert self .handle is not None # show type checkers we already checked
252266 assert self ._writable
253- hr , n_written = win32file .WriteFile (self .handle , b )
254- if hr :
255- raise _win_error (hr )
256- return n_written
267+ size = len (b )
268+ nwritten = wintypes .DWORD (0 )
269+ buf = (ctypes .c_char * size ).from_buffer_copy (b )
270+ if not ctypes .windll .kernel32 .WriteFile (self .handle , buf , size , ctypes .byref (nwritten ), None ):
271+ code = ctypes .GetLastError ()
272+ raise _win_error (code )
273+
274+ return nwritten .value
0 commit comments