Skip to content

Commit e85e813

Browse files
authored
gh-119690: Adds Unicode support for named pipes in _winapi (GH-119717)
Also backports a minor improvement to test_audit.
1 parent fae9c92 commit e85e813

File tree

6 files changed

+98
-35
lines changed

6 files changed

+98
-35
lines changed

Lib/test/audit-tests.py

Lines changed: 19 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -186,7 +186,7 @@ class C(A):
186186
)
187187

188188

189-
def test_open():
189+
def test_open(testfn):
190190
# SSLContext.load_dh_params uses _Py_fopen_obj rather than normal open()
191191
try:
192192
import ssl
@@ -199,11 +199,11 @@ def test_open():
199199
# All of them should fail
200200
with TestHook(raise_on_events={"open"}) as hook:
201201
for fn, *args in [
202-
(open, sys.argv[2], "r"),
202+
(open, testfn, "r"),
203203
(open, sys.executable, "rb"),
204204
(open, 3, "wb"),
205-
(open, sys.argv[2], "w", -1, None, None, None, False, lambda *a: 1),
206-
(load_dh_params, sys.argv[2]),
205+
(open, testfn, "w", -1, None, None, None, False, lambda *a: 1),
206+
(load_dh_params, testfn),
207207
]:
208208
if not fn:
209209
continue
@@ -216,11 +216,11 @@ def test_open():
216216
[
217217
i
218218
for i in [
219-
(sys.argv[2], "r"),
219+
(testfn, "r"),
220220
(sys.executable, "r"),
221221
(3, "w"),
222-
(sys.argv[2], "w"),
223-
(sys.argv[2], "rb") if load_dh_params else None,
222+
(testfn, "w"),
223+
(testfn, "rb") if load_dh_params else None,
224224
]
225225
if i is not None
226226
],
@@ -525,10 +525,21 @@ def hook(event, args):
525525
sys.monitoring.register_callback(1, 1, None)
526526

527527

528+
def test_winapi_createnamedpipe(pipe_name):
529+
import _winapi
530+
531+
def hook(event, args):
532+
if event == "_winapi.CreateNamedPipe":
533+
print(event, args)
534+
535+
sys.addaudithook(hook)
536+
_winapi.CreateNamedPipe(pipe_name, _winapi.PIPE_ACCESS_DUPLEX, 8, 2, 0, 0, 0, 0)
537+
538+
528539
if __name__ == "__main__":
529540
from test.support import suppress_msvcrt_asserts
530541

531542
suppress_msvcrt_asserts()
532543

533544
test = sys.argv[1]
534-
globals()[test]()
545+
globals()[test](*sys.argv[2:])

Lib/test/test_audit.py

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -269,6 +269,20 @@ def test_sys_monitoring_register_callback(self):
269269

270270
self.assertEqual(actual, expected)
271271

272+
def test_winapi_createnamedpipe(self):
273+
winapi = import_helper.import_module("_winapi")
274+
275+
pipe_name = r"\\.\pipe\LOCAL\test_winapi_createnamed_pipe"
276+
returncode, events, stderr = self.run_python("test_winapi_createnamedpipe", pipe_name)
277+
if returncode:
278+
self.fail(stderr)
279+
280+
if support.verbose:
281+
print(*events, sep='\n')
282+
actual = [(ev[0], ev[2]) for ev in events]
283+
expected = [("_winapi.CreateNamedPipe", f"({pipe_name!r}, 3, 8)")]
284+
285+
self.assertEqual(actual, expected)
272286

273287
if __name__ == "__main__":
274288
unittest.main()

Lib/test/test_winapi.py

Lines changed: 33 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
import pathlib
55
import re
66
import unittest
7-
from test.support import import_helper
7+
from test.support import import_helper, os_helper
88

99
_winapi = import_helper.import_module('_winapi', required_on=['win'])
1010

@@ -38,3 +38,35 @@ def test_getshortpathname(self):
3838

3939
# Should contain "PROGRA~" but we can't predict the number
4040
self.assertIsNotNone(re.match(r".\:\\PROGRA~\d", actual.upper()), actual)
41+
42+
def test_namedpipe(self):
43+
pipe_name = rf"\\.\pipe\LOCAL\{os_helper.TESTFN}"
44+
45+
# Pipe does not exist, so this raises
46+
with self.assertRaises(FileNotFoundError):
47+
_winapi.WaitNamedPipe(pipe_name, 0)
48+
49+
pipe = _winapi.CreateNamedPipe(
50+
pipe_name,
51+
_winapi.PIPE_ACCESS_DUPLEX,
52+
8, # 8=PIPE_REJECT_REMOTE_CLIENTS
53+
2, # two instances available
54+
32, 32, 0, 0)
55+
self.addCleanup(_winapi.CloseHandle, pipe)
56+
57+
# Pipe instance is available, so this passes
58+
_winapi.WaitNamedPipe(pipe_name, 0)
59+
60+
with open(pipe_name, 'w+b') as pipe2:
61+
# No instances available, so this times out
62+
# (WinError 121 does not get mapped to TimeoutError)
63+
with self.assertRaises(OSError):
64+
_winapi.WaitNamedPipe(pipe_name, 0)
65+
66+
_winapi.WriteFile(pipe, b'testdata')
67+
self.assertEqual(b'testdata', pipe2.read(8))
68+
69+
self.assertEqual((b'', 0), _winapi.PeekNamedPipe(pipe, 8)[:2])
70+
pipe2.write(b'testdata')
71+
pipe2.flush()
72+
self.assertEqual((b'testdata', 8), _winapi.PeekNamedPipe(pipe, 8)[:2])
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Adds Unicode support and fixes audit events for ``_winapi.CreateNamedPipe``.

Modules/_winapi.c

Lines changed: 16 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -186,7 +186,6 @@ create_converter('LPCVOID', '" F_POINTER "')
186186
187187
create_converter('BOOL', 'i') # F_BOOL used previously (always 'i')
188188
create_converter('DWORD', 'k') # F_DWORD is always "k" (which is much shorter)
189-
create_converter('LPCTSTR', 's')
190189
create_converter('UINT', 'I') # F_UINT used previously (always 'I')
191190
192191
class LPCWSTR_converter(Py_UNICODE_converter):
@@ -221,7 +220,7 @@ class LPVOID_return_converter(CReturnConverter):
221220
data.return_conversion.append(
222221
'return_value = HANDLE_TO_PYNUM(_return_value);\n')
223222
[python start generated code]*/
224-
/*[python end generated code: output=da39a3ee5e6b4b0d input=011ee0c3a2244bfe]*/
223+
/*[python end generated code: output=da39a3ee5e6b4b0d input=ae30321c4cb150dd]*/
225224

226225
#include "clinic/_winapi.c.h"
227226

@@ -459,7 +458,7 @@ _winapi_CreateFile_impl(PyObject *module, LPCWSTR file_name,
459458
{
460459
HANDLE handle;
461460

462-
if (PySys_Audit("_winapi.CreateFile", "uIIII",
461+
if (PySys_Audit("_winapi.CreateFile", "ukkkk",
463462
file_name, desired_access, share_mode,
464463
creation_disposition, flags_and_attributes) < 0) {
465464
return INVALID_HANDLE_VALUE;
@@ -675,7 +674,7 @@ _winapi_CreateJunction_impl(PyObject *module, LPCWSTR src_path,
675674
/*[clinic input]
676675
_winapi.CreateNamedPipe -> HANDLE
677676
678-
name: LPCTSTR
677+
name: LPCWSTR
679678
open_mode: DWORD
680679
pipe_mode: DWORD
681680
max_instances: DWORD
@@ -687,25 +686,25 @@ _winapi.CreateNamedPipe -> HANDLE
687686
[clinic start generated code]*/
688687

689688
static HANDLE
690-
_winapi_CreateNamedPipe_impl(PyObject *module, LPCTSTR name, DWORD open_mode,
689+
_winapi_CreateNamedPipe_impl(PyObject *module, LPCWSTR name, DWORD open_mode,
691690
DWORD pipe_mode, DWORD max_instances,
692691
DWORD out_buffer_size, DWORD in_buffer_size,
693692
DWORD default_timeout,
694693
LPSECURITY_ATTRIBUTES security_attributes)
695-
/*[clinic end generated code: output=80f8c07346a94fbc input=5a73530b84d8bc37]*/
694+
/*[clinic end generated code: output=7d6fde93227680ba input=5bd4e4a55639ee02]*/
696695
{
697696
HANDLE handle;
698697

699-
if (PySys_Audit("_winapi.CreateNamedPipe", "uII",
698+
if (PySys_Audit("_winapi.CreateNamedPipe", "ukk",
700699
name, open_mode, pipe_mode) < 0) {
701700
return INVALID_HANDLE_VALUE;
702701
}
703702

704703
Py_BEGIN_ALLOW_THREADS
705-
handle = CreateNamedPipe(name, open_mode, pipe_mode,
706-
max_instances, out_buffer_size,
707-
in_buffer_size, default_timeout,
708-
security_attributes);
704+
handle = CreateNamedPipeW(name, open_mode, pipe_mode,
705+
max_instances, out_buffer_size,
706+
in_buffer_size, default_timeout,
707+
security_attributes);
709708
Py_END_ALLOW_THREADS
710709

711710
if (handle == INVALID_HANDLE_VALUE)
@@ -1720,7 +1719,7 @@ _winapi_OpenProcess_impl(PyObject *module, DWORD desired_access,
17201719
{
17211720
HANDLE handle;
17221721

1723-
if (PySys_Audit("_winapi.OpenProcess", "II",
1722+
if (PySys_Audit("_winapi.OpenProcess", "kk",
17241723
process_id, desired_access) < 0) {
17251724
return INVALID_HANDLE_VALUE;
17261725
}
@@ -2005,19 +2004,19 @@ _winapi_VirtualQuerySize_impl(PyObject *module, LPCVOID address)
20052004
/*[clinic input]
20062005
_winapi.WaitNamedPipe
20072006
2008-
name: LPCTSTR
2007+
name: LPCWSTR
20092008
timeout: DWORD
20102009
/
20112010
[clinic start generated code]*/
20122011

20132012
static PyObject *
2014-
_winapi_WaitNamedPipe_impl(PyObject *module, LPCTSTR name, DWORD timeout)
2015-
/*[clinic end generated code: output=c2866f4439b1fe38 input=36fc781291b1862c]*/
2013+
_winapi_WaitNamedPipe_impl(PyObject *module, LPCWSTR name, DWORD timeout)
2014+
/*[clinic end generated code: output=e161e2e630b3e9c2 input=099a4746544488fa]*/
20162015
{
20172016
BOOL success;
20182017

20192018
Py_BEGIN_ALLOW_THREADS
2020-
success = WaitNamedPipe(name, timeout);
2019+
success = WaitNamedPipeW(name, timeout);
20212020
Py_END_ALLOW_THREADS
20222021

20232022
if (!success)
@@ -2382,7 +2381,7 @@ _winapi_CopyFile2_impl(PyObject *module, LPCWSTR existing_file_name,
23822381
HRESULT hr;
23832382
COPYFILE2_EXTENDED_PARAMETERS params = { sizeof(COPYFILE2_EXTENDED_PARAMETERS) };
23842383

2385-
if (PySys_Audit("_winapi.CopyFile2", "uuI",
2384+
if (PySys_Audit("_winapi.CopyFile2", "uuk",
23862385
existing_file_name, new_file_name, flags) < 0) {
23872386
return NULL;
23882387
}

Modules/clinic/_winapi.c.h

Lines changed: 15 additions & 9 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)