1
- import win32pipe , win32file , win32api , winerror , win32con , pywintypes
1
+ import ctypes
2
+ from ctypes import wintypes
2
3
from os import path
3
4
import io
4
5
from typing import TypeVar , NewType , Literal , IO , Optional , Union
5
6
6
7
WritableBuffer = TypeVar ("WritableBuffer" )
7
8
PyHANDLE = NewType ("PyHANDLE" , int )
8
9
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
+
9
25
id = 0
10
26
27
+ def _wt (value : int ) -> wintypes .DWORD :
28
+ return wintypes .DWORD (value )
11
29
12
30
def _name_pipe ():
13
31
global id
@@ -23,11 +41,18 @@ def _name_pipe():
23
41
24
42
def _win_error (code = None ):
25
43
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
29
54
)
30
-
55
+ return OSError ( f"[OS Error { code } ] { buf . value } " )
31
56
32
57
class NPopen :
33
58
def __init__ (
@@ -72,30 +97,26 @@ def __init__(
72
97
)
73
98
74
99
self ._bufsize = - 1 if txt_mode and bufsize == 1 else bufsize
75
-
76
100
if self ._rd and self ._wr :
77
- access = win32pipe . PIPE_ACCESS_DUPLEX
101
+ access = _wt ( PIPE_ACCESS_DUPLEX )
78
102
elif self ._rd :
79
- access = win32pipe . PIPE_ACCESS_INBOUND
103
+ access = _wt ( PIPE_ACCESS_INBOUND )
80
104
elif self ._wr :
81
- access = win32pipe . PIPE_ACCESS_OUTBOUND
105
+ access = _wt ( PIPE_ACCESS_OUTBOUND )
82
106
else :
83
107
raise ValueError ("Invalid mode" )
84
108
# 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 )
85
110
86
- pipe_mode = (
87
- win32pipe .PIPE_TYPE_BYTE
88
- | win32pipe .PIPE_READMODE_BYTE
89
- | win32pipe .PIPE_WAIT
90
- )
91
111
# TODO: assess options: PIPE_WAIT, PIPE_NOWAIT, PIPE_ACCEPT_REMOTE_CLIENTS, PIPE_REJECT_REMOTE_CLIENTS
92
112
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
+
96
117
97
118
# "open" named pipe
98
- self ._pipe = win32pipe . CreateNamedPipe (
119
+ self ._pipe = ctypes . windll . kernel32 . CreateNamedPipeW (
99
120
self ._path ,
100
121
access ,
101
122
pipe_mode ,
@@ -105,7 +126,7 @@ def __init__(
105
126
timeout ,
106
127
None ,
107
128
)
108
- if self ._pipe == win32file . INVALID_HANDLE_VALUE :
129
+ if self ._pipe == INVALID_HANDLE_VALUE :
109
130
raise _win_error ()
110
131
111
132
@property
@@ -114,28 +135,28 @@ def path(self):
114
135
return self ._path
115
136
116
137
def __str__ (self ):
117
- # return the path
118
138
return self ._path
119
139
120
140
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
+ """
122
145
if self .stream is not None :
123
146
self .stream .close ()
124
147
self .stream = None
125
148
if self ._pipe is not None :
126
- if win32file .CloseHandle (self ._pipe ):
149
+ if not ctypes . windll . kernel32 .CloseHandle (self ._pipe ):
127
150
raise _win_error ()
128
151
self ._pipe = None
129
152
130
153
def wait (self ):
131
-
154
+ """Wait for the pipe to open (the other end to be opened) and return file object to read/write."""
132
155
if not self ._pipe :
133
156
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)
139
160
raise _win_error (code )
140
161
141
162
# create new io stream object
@@ -179,15 +200,15 @@ def writable(self) -> bool:
179
200
class Win32RawIO (io .RawIOBase ):
180
201
"""Raw I/O stream layer over open Windows pipe handle.
181
202
182
- `handle` is an open ``pywintypes.PyHANDLE `` object (from ``pywin32 `` package) to
203
+ `handle` is an open Windows ``HANDLE `` object (from ``ctype `` package) to
183
204
be wrapped by this class.
184
205
185
206
Specify the read and write modes by boolean flags: ``rd`` and ``wr``.
186
207
"""
187
208
188
209
def __init__ (self , handle : PyHANDLE , rd : bool , wr : bool ) -> None :
189
210
super ().__init__ ()
190
- self .handle = handle # PyHANDLE: Underlying Windows handle.
211
+ self .handle = handle # Underlying Windows handle.
191
212
self ._readable : bool = rd
192
213
self ._writable : bool = wr
193
214
@@ -211,7 +232,7 @@ def close(self) -> None:
211
232
if self .closed :
212
233
return
213
234
if self .handle is not None :
214
- win32file .CloseHandle (self .handle )
235
+ ctypes . windll . kernel32 .CloseHandle (self .handle )
215
236
self .handle = None
216
237
217
238
super ().close ()
@@ -224,23 +245,16 @@ def readinto(self, b: WritableBuffer) -> Union[int, None]:
224
245
assert self ._readable
225
246
226
247
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
244
258
245
259
def write (self , b ):
246
260
"""Write buffer ``b`` to file, return number of bytes written.
@@ -250,7 +264,11 @@ def write(self, b):
250
264
251
265
assert self .handle is not None # show type checkers we already checked
252
266
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