Skip to content

Commit e87c0d2

Browse files
in-code-i-trustslozier
authored andcommitted
Making test_winreg passes. (#718)
* Implement `_set_sentinel` and timeout for `lock.acquire()` * Added `DeleteKeyEx()`. * Making keyword arguments work. e.g.) OpenKey(sub_key="bar") * Properly handle sub_key length in `DeleteKey()`. * Any failures in `DeleteKey()` should raise OSError. This behavior is required to make `test_winreg` passes. * Properly handle REG_BINARY type in `SetValueExt()` and `EnumValue()`. * Enabled `test_winreg`. * Added a threading test. This test was introduced in GitHub issue 410 and resolved in commit 972a7bd.
1 parent 0e09faf commit e87c0d2

4 files changed

Lines changed: 115 additions & 57 deletions

File tree

Src/IronPython.Modules/_thread.cs

Lines changed: 20 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ public static class PythonThread {
2323

2424
private static readonly object _stackSizeKey = new object();
2525
private static object _threadCountKey = new object();
26+
[ThreadStatic] private static List<@lock> _sentinelLocks;
2627

2728
[SpecialName]
2829
public static void PerformModuleReload(PythonContext/*!*/ context, PythonDictionary/*!*/ dict) {
@@ -123,8 +124,12 @@ public static int _count(CodeContext context) {
123124

124125
[Documentation("_set_sentinel() -> lock\n\nSet a sentinel lock that will be released when the current thread\nstate is finalized (after it is untied from the interpreter).\n\nThis is a private API for the threading module.")]
125126
public static object _set_sentinel(CodeContext context) {
126-
// TODO: properly implement this
127-
return new @lock();
127+
if (_sentinelLocks == null) {
128+
_sentinelLocks = new List<@lock>();
129+
}
130+
var obj = new @lock();
131+
_sentinelLocks.Add(obj);
132+
return obj;
128133
}
129134

130135
#endregion
@@ -144,7 +149,6 @@ public void __exit__(CodeContext/*!*/ context, params object[] args) {
144149
}
145150

146151
public bool acquire(bool blocking=true, int timeout=-1) {
147-
// TODO: implement timeout
148152
for (; ; ) {
149153
if (Interlocked.CompareExchange<Thread>(ref curHolder, Thread.CurrentThread, null) == null) {
150154
return true;
@@ -158,7 +162,9 @@ public bool acquire(bool blocking=true, int timeout=-1) {
158162
CreateBlockEvent();
159163
continue;
160164
}
161-
blockEvent.WaitOne();
165+
if (!blockEvent.WaitOne(timeout < 0 ? Timeout.Infinite : timeout)) {
166+
return false;
167+
}
162168
GC.KeepAlive(this);
163169
}
164170
}
@@ -234,10 +240,19 @@ public void Start() {
234240
int curCount = (int)_context.LanguageContext.GetModuleState(_threadCountKey);
235241
_context.LanguageContext.SetModuleState(_threadCountKey, curCount - 1);
236242
}
243+
244+
// release sentinel locks if locked.
245+
if (_sentinelLocks != null) {
246+
foreach (var obj in _sentinelLocks) {
247+
if (obj.locked()) {
248+
obj.release(_context);
249+
}
250+
}
251+
_sentinelLocks.Clear();
252+
}
237253
}
238254
}
239255
}
240-
241256
#endregion
242257

243258
private static int GetStackSize(CodeContext/*!*/ context) {

Src/IronPython.Modules/winreg.cs

Lines changed: 81 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,8 @@ public static class PythonWinReg {
5050
public const int KEY_EXECUTE = 0X20019;
5151
public const int KEY_READ = 0X20019;
5252
public const int KEY_WRITE = 0X20006;
53+
public const int KEY_WOW64_64KEY = 0X100;
54+
public const int KEY_WOW64_32KEY = 0X200;
5355

5456
public const int REG_CREATED_NEW_KEY = 0X1;
5557
public const int REG_OPENED_EXISTING_KEY = 0X2;
@@ -93,42 +95,42 @@ public static void CloseKey(HKEYType key) {
9395
key.Close();
9496
}
9597

96-
public static HKEYType CreateKey(object key, string subKeyName) {
98+
public static HKEYType CreateKey(object key, string sub_key) {
9799
// the .NET APIs don't work with a key name of length 256, use CreateKeyEx which PInvokes instead
98-
if (subKeyName.Length == 256)
99-
return CreateKeyEx(key, subKeyName, 0, KEY_ALL_ACCESS);
100+
if (sub_key.Length == 256)
101+
return CreateKeyEx(key, sub_key, 0, KEY_ALL_ACCESS);
100102

101103
HKEYType rootKey = GetRootKey(key);
102104

103105
//if key is a system key and no subkey is specified return that.
104-
if (key is BigInteger && string.IsNullOrEmpty(subKeyName))
106+
if (key is BigInteger && string.IsNullOrEmpty(sub_key))
105107
return rootKey;
106108

107-
HKEYType subKey = new HKEYType(rootKey.GetKey().CreateSubKey(subKeyName));
109+
HKEYType subKey = new HKEYType(rootKey.GetKey().CreateSubKey(sub_key));
108110
return subKey;
109111
}
110112

111113
private static string FormatError(int errorCode) {
112114
return new Win32Exception(errorCode).Message;
113115
}
114116

115-
public static HKEYType CreateKeyEx(object key, string subKeyName, int res = 0, int sam = KEY_ALL_ACCESS) {
117+
public static HKEYType CreateKeyEx(object key, string sub_key, int reserved = 0, int access = KEY_ALL_ACCESS) {
116118
HKEYType rootKey = GetRootKey(key);
117119

118120
//if key is a system key and no subkey is specified return that.
119-
if (key is BigInteger && string.IsNullOrEmpty(subKeyName))
121+
if (key is BigInteger && string.IsNullOrEmpty(sub_key))
120122
return rootKey;
121123

122124
SafeRegistryHandle handle;
123125
int disposition;
124126

125127
int result = RegCreateKeyEx(
126128
rootKey.GetKey().Handle,
127-
subKeyName,
129+
sub_key,
128130
0,
129131
null,
130132
RegistryOptions.None,
131-
(RegistryRights)sam,
133+
(RegistryRights)access,
132134
IntPtr.Zero,
133135
out handle,
134136
out disposition
@@ -187,6 +189,13 @@ internal static extern int RegDeleteKey(
187189
SafeRegistryHandle hKey,
188190
string lpSubKey);
189191

192+
[DllImport("advapi32.dll", CharSet = CharSet.Unicode)]
193+
internal static extern int RegDeleteKeyEx(
194+
SafeRegistryHandle hKey,
195+
string lpSubKey,
196+
int samDesired,
197+
int Reserved);
198+
190199
[DllImport("advapi32.dll", CharSet = CharSet.Unicode)]
191200
internal static extern int RegDisableReflectionKey(
192201
SafeRegistryHandle hKey);
@@ -200,22 +209,38 @@ internal static extern int RegQueryReflectionKey(
200209
SafeRegistryHandle hBase,
201210
out bool bIsReflectionDisabled);
202211

203-
public static void DeleteKey(object key, string subKeyName) {
212+
public static void DeleteKey(object key, string sub_key) {
204213
HKEYType rootKey = GetRootKey(key);
205214

206-
if (key is BigInteger && string.IsNullOrEmpty(subKeyName))
215+
if (key is BigInteger && string.IsNullOrEmpty(sub_key))
207216
throw new InvalidCastException("DeleteKey() argument 2 must be string, not None");
208217

209218
// the .NET APIs don't work with a key name of length 256, use a PInvoke instead
210-
if (subKeyName.Length == 256) {
211-
RegDeleteKey(rootKey.GetKey().Handle, subKeyName);
219+
// unlike in CreateKey(), `subKeyName.Length` on deletion can be greater than 256 if combined with parent key name.
220+
if (sub_key.Length >= 256) {
221+
int result = RegDeleteKey(rootKey.GetKey().Handle, sub_key);
222+
if (result != ERROR_SUCCESS) {
223+
throw PythonExceptions.CreateThrowable(PythonExceptions.OSError, result);
224+
}
212225
return;
213226
}
214227

215228
try {
216-
rootKey.GetKey().DeleteSubKey(subKeyName);
229+
rootKey.GetKey().DeleteSubKey(sub_key);
217230
} catch (ArgumentException e) {
218-
throw new ExternalException(e.Message);
231+
throw PythonExceptions.CreateThrowable(PythonExceptions.OSError, e.HResult);
232+
}
233+
}
234+
235+
public static void DeleteKeyEx(object key, string sub_key, int access=KEY_WOW64_64KEY, int reserved=0) {
236+
HKEYType rootKey = GetRootKey(key);
237+
238+
if (key is BigInteger && string.IsNullOrEmpty(sub_key))
239+
throw new InvalidCastException("DeleteKeyEx() argument 2 must be string, not None");
240+
241+
int result = RegDeleteKeyEx(rootKey.GetKey().Handle, sub_key, access, reserved);
242+
if (result != ERROR_SUCCESS) {
243+
throw PythonExceptions.CreateThrowable(PythonExceptions.OSError, result);
219244
}
220245
}
221246

@@ -311,7 +336,11 @@ private static void QueryValueExImpl(SafeRegistryHandle handle, string valueName
311336
value = list;
312337
break;
313338
case REG_BINARY:
314-
value = length == 0 ? null : PythonOps.MakeString(data, (int)length);
339+
var tight_fit_data = new byte[length];
340+
for (int i = 0; i < length; i++) {
341+
tight_fit_data[i] = data[i];
342+
}
343+
value = length == 0 ? null : new Bytes(tight_fit_data);
315344
break;
316345
case REG_EXPAND_SZ:
317346
case REG_SZ:
@@ -359,11 +388,11 @@ public static void FlushKey(object key) {
359388
rootKey.GetKey().Flush();
360389
}
361390

362-
public static HKEYType OpenKey(object key, string subKeyName) {
363-
return OpenKey(key, subKeyName, 0, KEY_READ);
391+
public static HKEYType OpenKey(object key, string sub_key) {
392+
return OpenKey(key, sub_key, 0, KEY_READ);
364393
}
365394

366-
public static HKEYType OpenKey(object key, string subKeyName, int res=0, int sam=KEY_READ) {
395+
public static HKEYType OpenKey(object key, string sub_key, int reserved=0, int access=KEY_READ) {
367396
HKEYType rootKey = GetRootKey(key);
368397
RegistryKey newKey = null;
369398

@@ -377,20 +406,20 @@ public static HKEYType OpenKey(object key, string subKeyName, int res=0, int sam
377406

378407
var nativeRootKey = rootKey.GetKey();
379408
try {
380-
if ((sam & KEY_SET_VALUE) == KEY_SET_VALUE ||
381-
(sam & KEY_CREATE_SUB_KEY) == KEY_CREATE_SUB_KEY) {
382-
if (res != 0) {
383-
newKey = nativeRootKey.OpenSubKey(subKeyName, RegistryKeyPermissionCheck.Default, (RegistryRights)res);
409+
if ((access & KEY_SET_VALUE) == KEY_SET_VALUE ||
410+
(access & KEY_CREATE_SUB_KEY) == KEY_CREATE_SUB_KEY) {
411+
if (reserved != 0) {
412+
newKey = nativeRootKey.OpenSubKey(sub_key, RegistryKeyPermissionCheck.Default, (RegistryRights)reserved);
384413
} else {
385-
newKey = nativeRootKey.OpenSubKey(subKeyName, true);
414+
newKey = nativeRootKey.OpenSubKey(sub_key, true);
386415
}
387-
} else if ((sam & KEY_QUERY_VALUE) == KEY_QUERY_VALUE ||
388-
(sam & KEY_ENUMERATE_SUB_KEYS) == KEY_ENUMERATE_SUB_KEYS ||
389-
(sam & KEY_NOTIFY) == KEY_NOTIFY) {
390-
if (res != 0) {
391-
newKey = nativeRootKey.OpenSubKey(subKeyName, RegistryKeyPermissionCheck.ReadSubTree, (RegistryRights)res);
416+
} else if ((access & KEY_QUERY_VALUE) == KEY_QUERY_VALUE ||
417+
(access & KEY_ENUMERATE_SUB_KEYS) == KEY_ENUMERATE_SUB_KEYS ||
418+
(access & KEY_NOTIFY) == KEY_NOTIFY) {
419+
if (reserved != 0) {
420+
newKey = nativeRootKey.OpenSubKey(sub_key, RegistryKeyPermissionCheck.ReadSubTree, (RegistryRights)reserved);
392421
} else {
393-
newKey = nativeRootKey.OpenSubKey(subKeyName, false);
422+
newKey = nativeRootKey.OpenSubKey(sub_key, false);
394423
}
395424
} else {
396425
throw new Win32Exception("Unexpected mode");
@@ -407,8 +436,8 @@ public static HKEYType OpenKey(object key, string subKeyName, int res=0, int sam
407436
return new HKEYType(newKey);
408437
}
409438

410-
public static HKEYType OpenKeyEx(object key, string subKeyName, int res=0, int sam=KEY_READ) {
411-
return OpenKey(key, subKeyName, res, sam);
439+
public static HKEYType OpenKeyEx(object key, string sub_key, int reserved=0, int access=KEY_READ) {
440+
return OpenKey(key, sub_key, reserved, access);
412441
}
413442

414443
public static PythonTuple QueryInfoKey(object key) {
@@ -436,83 +465,85 @@ public static PythonTuple QueryInfoKey(object key) {
436465
}
437466
}
438467

439-
public static object QueryValue(object key, string subKeyName) {
440-
HKEYType pyKey = OpenKey(key, subKeyName);
468+
public static object QueryValue(object key, string sub_key) {
469+
HKEYType pyKey = OpenKey(key, sub_key);
441470
return pyKey.GetKey().GetValue(null);
442471
}
443472

444-
public static PythonTuple QueryValueEx(object key, string valueName) {
473+
public static PythonTuple QueryValueEx(object key, string value_name) {
445474
HKEYType rootKey = GetRootKey(key);
446475

447476
// it looks like rootKey.GetKey().Handle fails on HKEY_PERFORMANCE_DATA so manually create the handle instead
448477
var handle = rootKey.hkey == HKEY_PERFORMANCE_DATA ? new SafeRegistryHandle(new IntPtr(unchecked((int)0x80000004)), true) : rootKey.GetKey().Handle;
449478

450479
int valueKind;
451480
object value;
452-
QueryValueExImpl(handle, valueName, out valueKind, out value);
481+
QueryValueExImpl(handle, value_name, out valueKind, out value);
453482

454483
return PythonTuple.MakeTuple(value, valueKind);
455484
}
456485

457-
public static void SetValue(object key, string subKeyName, int type, string value) {
458-
HKEYType pyKey = CreateKey(key, subKeyName);
486+
public static void SetValue(object key, string sub_key, int type, string value) {
487+
HKEYType pyKey = CreateKey(key, sub_key);
459488
pyKey.GetKey().SetValue(null, value);
460489
}
461490

462-
public static void SetValueEx(object key, string valueName, object reserved, int type, object value) {
491+
public static void SetValueEx(object key, string value_name, object reserved, int type, object value) {
463492
HKEYType rootKey = GetRootKey(key);
464493
RegistryValueKind regKind = (RegistryValueKind)type;
465494

466495
// null is a valid value but RegistryKey.SetValue doesn't like it so PInvoke to set it
467496
if (value == null) {
468-
RegSetValueEx(rootKey.GetKey().Handle, valueName, 0, type, null, 0);
497+
RegSetValueEx(rootKey.GetKey().Handle, value_name, 0, type, null, 0);
469498
return;
470499
}
471500

472501
if (regKind == RegistryValueKind.MultiString) {
473502
int size = ((PythonList)value)._size;
474503
string[] strArray = new string[size];
475504
Array.Copy(((PythonList)value)._data, strArray, size);
476-
rootKey.GetKey().SetValue(valueName, strArray, regKind);
505+
rootKey.GetKey().SetValue(value_name, strArray, regKind);
477506
} else if (regKind == RegistryValueKind.Binary) {
478507
byte[] byteArr = null;
479508
if (value is string) {
480509
string strValue = value as string;
481510
ASCIIEncoding encoding = new ASCIIEncoding();
482511
byteArr = encoding.GetBytes(strValue);
512+
} else if (value is Bytes ibytes) {
513+
byteArr = ibytes.UnsafeByteArray;
483514
}
484-
rootKey.GetKey().SetValue(valueName, byteArr, regKind);
515+
rootKey.GetKey().SetValue(value_name, byteArr, regKind);
485516
} else if (regKind == RegistryValueKind.DWord) {
486517
// DWORDs are uint but the .NET API requires int
487518
if (value is BigInteger) {
488519
var val = (uint)(BigInteger)value;
489520
value = unchecked((int)val);
490521
}
491-
rootKey.GetKey().SetValue(valueName, value, regKind);
522+
rootKey.GetKey().SetValue(value_name, value, regKind);
492523
} else if (regKind == RegistryValueKind.QWord) {
493524
// QWORDs are ulong but the .NET API requires long
494525
if (value is BigInteger) {
495526
var val = (ulong)(BigInteger)value;
496527
value = unchecked((long)val);
497528
}
498-
rootKey.GetKey().SetValue(valueName, value, regKind);
529+
rootKey.GetKey().SetValue(value_name, value, regKind);
499530
} else {
500-
rootKey.GetKey().SetValue(valueName, value, regKind);
531+
rootKey.GetKey().SetValue(value_name, value, regKind);
501532
}
502533

503534
}
504535

505-
public static HKEYType ConnectRegistry(string computerName, BigInteger key) {
506-
if (string.IsNullOrEmpty(computerName))
507-
computerName = string.Empty;
536+
public static HKEYType ConnectRegistry(string computer_name, BigInteger key) {
537+
if (string.IsNullOrEmpty(computer_name))
538+
computer_name = string.Empty;
508539

509540
RegistryKey newKey = null;
510541
try {
511-
if (computerName == string.Empty) {
542+
if (computer_name == string.Empty) {
512543
newKey = RegistryKey.OpenBaseKey(MapSystemKey(key), RegistryView.Default);
513544
}
514545
else {
515-
newKey = RegistryKey.OpenRemoteBaseKey(MapSystemKey(key), computerName);
546+
newKey = RegistryKey.OpenRemoteBaseKey(MapSystemKey(key), computer_name);
516547
}
517548
}
518549
catch (IOException ioe) {

Src/IronPythonTest/Cases/AllCPythonCasesManifest.ini

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1049,8 +1049,6 @@ Reason=https://github.com/IronLanguages/ironpython3/issues/489
10491049

10501050
[AllCPython.test_winreg]
10511051
RunCondition=$(IS_WINDOWS)
1052-
Ignore=true
1053-
Reason=https://github.com/IronLanguages/ironpython3/issues/410
10541052

10551053
[AllCPython.test_winsound]
10561054
RunCondition=$(IS_WINDOWS)

Tests/test_regressions.py

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1435,4 +1435,18 @@ def test_ipy2_gh655(self):
14351435
result = ET.tostring(ET.fromstring(txt))
14361436
self.assertEqual(txt, result)
14371437

1438+
def test_ipy3_gh410(self):
1439+
"""https://github.com/IronLanguages/ironpython3/issues/410"""
1440+
import threading
1441+
def test_thread():
1442+
class Thread(threading.Thread):
1443+
def run(self):
1444+
self.thread_executed = 1
1445+
1446+
thread = Thread()
1447+
thread.start()
1448+
thread.join()
1449+
self.assertEqual(thread.thread_executed, 1)
1450+
test_thread()
1451+
14381452
run_test(__name__)

0 commit comments

Comments
 (0)