Windows Registry Unicode Support and PY2 character encoding (module.reg) (#32835)

* reg.py
* Start at PY3 support, however not tested against PY3
* Change to Unicode including from __future__ import unicode_literals
* If PY2 converts all parameter input unicode to local encoding i.e. Unicode to String
* If PY3 stays as Unicode as PY3 uses Windows Wide Char
* Supports non-asiic characters i.e. > 126
* All output is Unicode, before most of it was Unicode output
* Added a safty check to recursive delete to try and prevent a mistake like removing all of SOFTWARE
* Fixed all the pylint errors
* add _ prefix to internal functions
* Provided unit test see reg_win_test.py
reg_win_test.py
* All tests currently make real changes to the registry
* All changes are performed under SOFTWARE\SaltStackTest under HKEY_LOCAL_MACHINE and HKEY_CURRENT_USER
* Their is still room for more tests to be developed
* Test target the new Unicode features of reg.py e.g. Copyright, Tradmark and Register characters
* General the values set in the registy contain date/time and then checked that they are the current date/time to make sure they are not left over from old tests

* Update test unit to only run the tests on windows.
Removed some code which was commented out which is not required

* chmod 644 tests/unit/modules/reg_win_test.py
This commit is contained in:
Damon Atkins 2016-04-27 03:07:05 +10:00 committed by Nicole Thomas
parent acb0c373be
commit cf4826aad4
2 changed files with 471 additions and 74 deletions

View File

@ -26,11 +26,16 @@ Values/Entries are name/data pairs. There can be many values in a key. The
:depends: - winreg Python module
'''
# Import python libs
from __future__ import absolute_import
import logging
from salt.ext.six.moves import range
# When production windows installer is using Python 3, Python 2 code can be removed
# Import _future_ python libs first & before any other code
from __future__ import absolute_import
from __future__ import unicode_literals
# Import python libs
import sys
import logging
from salt.ext.six.moves import range # pylint: disable=W0622
import salt.ext.six as six
# Import third party libs
try:
from salt.ext.six.moves import winreg as _winreg # pylint: disable=import-error,no-name-in-module
@ -44,6 +49,7 @@ except ImportError:
import salt.utils
from salt.exceptions import CommandExecutionError
PY2 = sys.version_info[0] == 2
log = logging.getLogger(__name__)
# Define the module's virtual name
@ -65,18 +71,67 @@ def __virtual__():
return __virtualname__
class Registry(object):
# winreg in python 2 is hard coded to use codex 'mbcs', which uses
# encoding that the user has assign. The function _unicode_to_mbcs
# and _unicode_to_mbcs help with this.
def _unicode_to_mbcs(instr):
'''
Converts unicode to to current users character encoding
'''
if isinstance(instr, six.text_type):
# unicode to windows utf8
return instr.encode('mbcs')
else:
# Assume its byte str or not a str/unicode
return instr
def _mbcs_to_unicode(instr):
'''
Converts from current users character encoding to unicode
'''
if isinstance(instr, six.text_type):
return instr
else:
return unicode(instr, 'mbcs')
class Registry(object): # pylint: disable=R0903
'''
Delay '_winreg' usage until this module is used
'''
def __init__(self):
self.hkeys = {
"HKEY_CURRENT_USER": _winreg.HKEY_CURRENT_USER,
"HKEY_LOCAL_MACHINE": _winreg.HKEY_LOCAL_MACHINE,
"HKEY_USERS": _winreg.HKEY_USERS,
"HKCU": _winreg.HKEY_CURRENT_USER,
"HKLM": _winreg.HKEY_LOCAL_MACHINE,
"HKU": _winreg.HKEY_USERS,
'HKEY_CURRENT_USER': _winreg.HKEY_CURRENT_USER,
'HKEY_LOCAL_MACHINE': _winreg.HKEY_LOCAL_MACHINE,
'HKEY_USERS': _winreg.HKEY_USERS,
'HKCU': _winreg.HKEY_CURRENT_USER,
'HKLM': _winreg.HKEY_LOCAL_MACHINE,
'HKU': _winreg.HKEY_USERS,
}
self.vtype = {
'REG_BINARY': _winreg.REG_BINARY,
'REG_DWORD': _winreg.REG_DWORD,
'REG_EXPAND_SZ': _winreg.REG_EXPAND_SZ,
'REG_MULTI_SZ': _winreg.REG_MULTI_SZ,
'REG_SZ': _winreg.REG_SZ
}
# Return Unicode due to from __future__ import unicode_literals
self.vtype_reverse = {
_winreg.REG_BINARY: 'REG_BINARY',
_winreg.REG_DWORD: 'REG_DWORD',
_winreg.REG_EXPAND_SZ: 'REG_EXPAND_SZ',
_winreg.REG_MULTI_SZ: 'REG_MULTI_SZ',
_winreg.REG_SZ: 'REG_SZ'
}
# delete_key_recursive uses this to check the subkey contains enough \
# as we do not want to remove all or most of the registry
self.subkey_slash_check = {
_winreg.HKEY_CURRENT_USER: 0,
_winreg.HKEY_LOCAL_MACHINE: 1,
_winreg.HKEY_USERS: 1
}
self.registry_32 = {
@ -84,22 +139,6 @@ class Registry(object):
False: _winreg.KEY_ALL_ACCESS,
}
self.vtype = {
"REG_BINARY": _winreg.REG_BINARY,
"REG_DWORD": _winreg.REG_DWORD,
"REG_EXPAND_SZ": _winreg.REG_EXPAND_SZ,
"REG_MULTI_SZ": _winreg.REG_MULTI_SZ,
"REG_SZ": _winreg.REG_SZ
}
self.vtype_reverse = {
_winreg.REG_BINARY: "REG_BINARY",
_winreg.REG_DWORD: "REG_DWORD",
_winreg.REG_EXPAND_SZ: "REG_EXPAND_SZ",
_winreg.REG_MULTI_SZ: "REG_MULTI_SZ",
_winreg.REG_SZ: "REG_SZ"
}
def __getattr__(self, k):
try:
return self.hkeys[k]
@ -120,12 +159,20 @@ def _key_exists(hive, key, use_32bit_registry=False):
:return: Returns True if found, False if not found
:rtype: bool
'''
if PY2:
local_hive = _mbcs_to_unicode(hive)
local_key = _unicode_to_mbcs(key)
else:
local_hive = hive
local_key = key
registry = Registry()
hkey = registry.hkeys[hive]
hkey = registry.hkeys[local_hive]
access_mask = registry.registry_32[use_32bit_registry]
try:
handle = _winreg.OpenKey(hkey, key, 0, access_mask)
handle = _winreg.OpenKey(hkey, local_key, 0, access_mask)
_winreg.CloseKey(handle)
return True
except WindowsError: # pylint: disable=E0602
@ -167,16 +214,27 @@ def list_keys(hive, key=None, use_32bit_registry=False):
salt '*' reg.list_keys HKLM 'SOFTWARE'
'''
if PY2:
local_hive = _mbcs_to_unicode(hive)
local_key = _unicode_to_mbcs(key)
else:
local_hive = hive
local_key = key
registry = Registry()
hkey = registry.hkeys[hive]
hkey = registry.hkeys[local_hive]
access_mask = registry.registry_32[use_32bit_registry]
subkeys = []
try:
handle = _winreg.OpenKey(hkey, key, 0, access_mask)
handle = _winreg.OpenKey(hkey, local_key, 0, access_mask)
for i in range(_winreg.QueryInfoKey(handle)[0]):
subkey = _winreg.EnumKey(handle, i)
if PY2:
subkeys.append(_mbcs_to_unicode(subkey))
else:
subkeys.append(subkey)
handle.Close()
@ -218,15 +276,12 @@ def read_key(hkey, path, key=None, use_32bit_registry=False):
salt '*' reg.read_key HKEY_LOCAL_MACHINE 'SOFTWARE\\Salt' 'version'
'''
ret = {'hive': hkey,
'key': path,
'vdata': None,
'success': True}
if key: # This if statement will be removed in Carbon
salt.utils.warn_until('Carbon', 'Use reg.read_value to read a registry '
'value. This functionality will be '
'removed in Salt Carbon')
salt.utils.warn_until(
'Carbon',
'Use reg.read_value to read a registry value. This functionality '
'will be removed in Salt Carbon'
)
return read_value(hive=hkey,
key=path,
vname=key,
@ -272,26 +327,42 @@ def read_value(hive, key, vname=None, use_32bit_registry=False):
salt '*' reg.read_value HKEY_LOCAL_MACHINE 'SOFTWARE\Salt' 'version'
'''
# If no name is passed, the default value of the key will be returned
# The value name is Default
# Setup the return array
if PY2:
ret = {'hive': _mbcs_to_unicode(hive),
'key': _mbcs_to_unicode(key),
'vname': _mbcs_to_unicode(vname),
'vdata': None,
'success': True}
local_hive = _mbcs_to_unicode(hive)
local_key = _unicode_to_mbcs(key)
local_vname = _unicode_to_mbcs(vname)
else:
ret = {'hive': hive,
'key': key,
'vname': vname,
'vdata': None,
'success': True}
local_hive = hive
local_key = key
local_vname = vname
# If no name is passed, the default value of the key will be returned
# The value name is Default
if not vname:
ret['vname'] = '(Default)'
registry = Registry()
hkey = registry.hkeys[hive]
hkey = registry.hkeys[local_hive]
access_mask = registry.registry_32[use_32bit_registry]
try:
handle = _winreg.OpenKey(hkey, key, 0, access_mask)
handle = _winreg.OpenKey(hkey, local_key, 0, access_mask)
try:
vdata, vtype = _winreg.QueryValueEx(handle, vname)
# QueryValueEx returns unicode data
vdata, vtype = _winreg.QueryValueEx(handle, local_vname)
if vdata or vdata in [0, '']:
ret['vtype'] = registry.vtype_reverse[vtype]
ret['vdata'] = vdata
@ -302,8 +373,8 @@ def read_value(hive, key, vname=None, use_32bit_registry=False):
ret['vtype'] = 'REG_SZ'
except WindowsError as exc: # pylint: disable=E0602
log.debug(exc)
log.debug('Cannot find key: {0}\\{1}'.format(hive, key))
ret['comment'] = 'Cannot find key: {0}\\{1}'.format(hive, key)
log.debug('Cannot find key: {0}\\{1}'.format(local_hive, local_key))
ret['comment'] = 'Cannot find key: {0}\\{1}'.format(local_hive, local_key)
ret['success'] = False
return ret
@ -351,17 +422,31 @@ def set_value(hive,
salt '*' reg.set_value HKEY_LOCAL_MACHINE 'SOFTWARE\\Salt' 'version' '2015.5.2'
'''
if PY2:
local_hive = _mbcs_to_unicode(hive)
local_key = _unicode_to_mbcs(key)
local_vname = _unicode_to_mbcs(vname)
local_vdata = _unicode_to_mbcs(vdata)
local_vtype = _mbcs_to_unicode(vtype)
else:
local_hive = hive
local_key = key
local_vname = vname
local_vdata = vdata
local_vtype = vtype
registry = Registry()
hkey = registry.hkeys[hive]
vtype = registry.vtype[vtype]
hkey = registry.hkeys[local_hive]
vtype_value = registry.vtype[local_vtype]
access_mask = registry.registry_32[use_32bit_registry]
try:
handle = _winreg.CreateKeyEx(hkey, key, 0, access_mask)
if vtype == registry.vtype['REG_SZ']\
or vtype == registry.vtype['REG_BINARY']:
vdata = str(vdata)
_winreg.SetValueEx(handle, vname, 0, vtype, vdata)
handle = _winreg.CreateKeyEx(hkey, local_key, 0, access_mask)
if vtype_value == registry.vtype['REG_SZ']\
or vtype_value == registry.vtype['REG_BINARY']:
local_vdata = str(local_vdata) # Not sure about this line
_winreg.SetValueEx(handle, local_vname, 0, vtype_value, local_vdata)
_winreg.FlushKey(handle)
_winreg.CloseKey(handle)
broadcast_change()
@ -401,17 +486,32 @@ def delete_key_recursive(hive, key, use_32bit_registry=False):
salt '*' reg.delete_key_recursive HKLM SOFTWARE\\salt
'''
if PY2:
local_hive = _mbcs_to_unicode(hive)
local_key = _unicode_to_mbcs(key)
else:
local_hive = hive
local_key = key
# Instantiate the registry object
registry = Registry()
hkey = registry.hkeys[hive]
key_path = key
hkey = registry.hkeys[local_hive]
key_path = local_key
access_mask = registry.registry_32[use_32bit_registry]
if not _key_exists(hive, key, use_32bit_registry):
if not _key_exists(local_hive, local_key, use_32bit_registry):
return False
if (len(key) > 1) and (key.count('\\', 1) < registry.subkey_slash_check[hkey]):
log.error('Hive:{0} Key:{1}; key is too close to root, not safe to remove'.format(hive, key))
return False
# Functions for traversing the registry tree
def subkeys(_key):
def _subkeys(_key):
'''
Enumerate keys
'''
i = 0
while True:
try:
@ -421,17 +521,20 @@ def delete_key_recursive(hive, key, use_32bit_registry=False):
except WindowsError: # pylint: disable=E0602
break
def traverse_registry_tree(_hkey, _keypath, _ret, _access_mask):
def _traverse_registry_tree(_hkey, _keypath, _ret, _access_mask):
'''
Traverse the registry tree i.e. dive into the tree
'''
_key = _winreg.OpenKey(_hkey, _keypath, 0, _access_mask)
for subkeyname in subkeys(_key):
for subkeyname in _subkeys(_key):
subkeypath = r'{0}\{1}'.format(_keypath, subkeyname)
_ret = traverse_registry_tree(_hkey, subkeypath, _ret, access_mask)
_ret = _traverse_registry_tree(_hkey, subkeypath, _ret, access_mask)
_ret.append('{0}'.format(subkeypath))
return _ret
# Get a reverse list of registry keys to be deleted
key_list = []
key_list = traverse_registry_tree(hkey, key_path, key_list, access_mask)
key_list = _traverse_registry_tree(hkey, key_path, key_list, access_mask)
# Add the top level key last, all subkeys must be deleted first
key_list.append(r'{0}'.format(key_path))
@ -480,20 +583,30 @@ def delete_value(hive, key, vname=None, use_32bit_registry=False):
salt '*' reg.delete_value HKEY_CURRENT_USER 'SOFTWARE\\Salt' 'version'
'''
if PY2:
local_hive = _mbcs_to_unicode(hive)
local_key = _unicode_to_mbcs(key)
local_vname = _unicode_to_mbcs(vname)
else:
local_hive = hive
local_key = key
local_vname = vname
registry = Registry()
h_hive = registry.hkeys[hive]
hkey = registry.hkeys[local_hive]
access_mask = registry.registry_32[use_32bit_registry]
try:
handle = _winreg.OpenKey(h_hive, key, 0, access_mask)
_winreg.DeleteValue(handle, vname)
handle = _winreg.OpenKey(hkey, local_key, 0, access_mask)
_winreg.DeleteValue(handle, local_vname)
_winreg.CloseKey(handle)
broadcast_change()
return True
except WindowsError as exc: # pylint: disable=E0602
log.error(exc, exc_info=True)
log.error('Hive: {0}'.format(hive))
log.error('Key: {0}'.format(key))
log.error('ValueName: {0}'.format(vname))
log.error('Hive: {0}'.format(local_hive))
log.error('Key: {0}'.format(local_key))
log.error('ValueName: {0}'.format(local_vname))
log.error('32bit Reg: {0}'.format(use_32bit_registry))
return False

View File

@ -0,0 +1,284 @@
# -*- coding: utf-8 -*-
'''
:synopsis: Unit Tests for Windows Registry Module 'module.reg'
:platform: Windows
:maturity: develop
:codeauthor: Damon Atkins <https://github.com/damon-atkins>
versionadded:: Carbon
'''
# Import Python future libs
from __future__ import absolute_import
from __future__ import unicode_literals
# Import Python Libs
import sys
import time
# Import Salt Testing Libs
from salttesting import TestCase, skipIf
from salttesting.helpers import destructiveTest
# Import Salt Libs
from salt.modules import reg as win_mod_reg
try:
from salt.ext.six.moves import winreg as _winreg # pylint: disable=import-error,no-name-in-module
NO_WINDOWS_MODULES = False
except ImportError:
NO_WINDOWS_MODULES = True
PY2 = sys.version_info[0] == 2
# The following used to make sure we are not
# testing already existing data
# Note strftime retunrns a str, so we need to make it unicode
TIMEINT = int(time.time())
if PY2:
TIME_INT_UNICODE = unicode(TIMEINT)
TIMESTR = time.strftime('%X %x %Z').decode('utf-8')
else:
TIMESTR = time.strftime('%X %x %Z')
TIME_INT_UNICODE = str(TIMEINT) # pylint: disable=R0204
# we do not need to prefix this with u, as we are
# using from __future__ import unicode_literals
UNICODETEST_WITH_SIGNS = 'Testing Unicode \N{COPYRIGHT SIGN},\N{TRADE MARK SIGN},\N{REGISTERED SIGN} '+TIMESTR
UNICODETEST_WITHOUT_SIGNS = 'Testing Unicode'+TIMESTR
UNICODE_TEST_KEY = 'UnicodeKey \N{TRADE MARK SIGN} '+TIME_INT_UNICODE
UNICODE_TEST_KEY_DEL = 'Delete Me \N{TRADE MARK SIGN} '+TIME_INT_UNICODE
@skipIf(NO_WINDOWS_MODULES, 'requires Windows OS to test Windows registry')
class RegWinTestCase(TestCase):
'''
Test cases for salt.modules.reg
'''
@skipIf(not sys.platform.startswith("win"), "requires Windows OS")
def test_read_reg_plain(self):
'''
Test - Read a registry value from a subkey using Pythen 2 Strings or
Pythen 3 Bytes
'''
if not PY2:
self.skipTest('Invalid for Python Version 2')
subkey = b'Software\\Microsoft\\Windows NT\\CurrentVersion'
vname = b'PathName'
handle = _winreg.OpenKey(
_winreg.HKEY_LOCAL_MACHINE,
subkey,
0,
_winreg.KEY_ALL_ACCESS
)
(current_vdata, dummy_current_vtype) = _winreg.QueryValueEx(handle, vname)
_winreg.CloseKey(handle)
test_vdata = win_mod_reg.read_value(b'HKEY_LOCAL_MACHINE', subkey, vname)[b'vdata']
self.assertEqual(
test_vdata, current_vdata)
@skipIf(not sys.platform.startswith("win"), "requires Windows OS")
def test_read_reg_unicode(self):
'''
Test - Read a registry value from a subkey using Pythen 2 Unicode
or Pythen 3 Str i.e. Unicode
'''
subkey = 'Software\\Microsoft\\Windows NT\\CurrentVersion'
vname = 'PathName'
handle = _winreg.OpenKey(
_winreg.HKEY_LOCAL_MACHINE,
subkey,
0,
_winreg.KEY_ALL_ACCESS
)
(current_vdata, dummy_current_vtype) = _winreg.QueryValueEx(handle, vname)
_winreg.CloseKey(handle)
test_vdata = win_mod_reg.read_value(
'HKEY_LOCAL_MACHINE',
subkey,
vname)['vdata']
self.assertEqual(test_vdata, current_vdata)
@skipIf(not sys.platform.startswith("win"), "requires Windows OS")
def test_list_keys_fail(self):
'''
Test - Read list the keys under a subkey which does not exist.
'''
subkey = 'ThisIsJunkItDoesNotExistIhope'
test_list = win_mod_reg.list_keys('HKEY_LOCAL_MACHINE', subkey)
# returns a tuple with first item false, and second item a reason
test = isinstance(test_list, tuple) and (not test_list[0])
self.assertTrue(test)
@skipIf(not sys.platform.startswith("win"), "requires Windows OS")
def test_list_keys(self):
'''
Test - Read list the keys under a subkey
'''
subkey = 'Software\\Microsoft\\Windows NT\\CurrentVersion'
test_list = win_mod_reg.list_keys('HKEY_LOCAL_MACHINE', subkey)
test = len(test_list) > 5 # Their should be a lot more than 5 items
self.assertTrue(test)
# Not considering this destructive as its writing to a private space
@skipIf(not sys.platform.startswith("win"), "requires Windows OS")
def test_set_value_unicode(self):
'''
Test - set a registry plain text subkey name to a unicode string value
'''
vname = 'TestUniccodeString'
subkey = 'Software\\SaltStackTest'
test1_success = False
test2_success = False
test1_success = win_mod_reg.set_value(
'HKEY_LOCAL_MACHINE',
subkey,
vname,
UNICODETEST_WITH_SIGNS
)
# Now use _winreg direct to see if it worked as expected
if test1_success:
handle = _winreg.OpenKey(
_winreg.HKEY_LOCAL_MACHINE,
subkey,
0,
_winreg.KEY_ALL_ACCESS
)
(current_vdata, dummy_current_vtype) = _winreg.QueryValueEx(handle, vname)
_winreg.CloseKey(handle)
test2_success = (current_vdata == UNICODETEST_WITH_SIGNS)
self.assertTrue(test1_success and test2_success)
@skipIf(not sys.platform.startswith("win"), "requires Windows OS")
def test_set_value_unicode_key(self):
'''
Test - set a registry Unicode subkey name with unicode characters within
to a integer
'''
test_success = win_mod_reg.set_value(
'HKEY_LOCAL_MACHINE',
'Software\\SaltStackTest',
UNICODE_TEST_KEY,
TIMEINT,
'REG_DWORD'
)
self.assertTrue(test_success)
@skipIf(not sys.platform.startswith("win"), "requires Windows OS")
def test_del_value(self):
'''
Test - Create Directly and Delete with salt a registry value
'''
subkey = 'Software\\SaltStackTest'
vname = UNICODE_TEST_KEY_DEL
vdata = 'I will be deleted'
if PY2:
handle = _winreg.CreateKeyEx(
_winreg.HKEY_LOCAL_MACHINE,
subkey.encode('mbcs'),
0,
_winreg.KEY_ALL_ACCESS
)
_winreg.SetValueEx(
handle,
vname.encode('mbcs'),
0,
_winreg.REG_SZ,
vdata.encode('mbcs')
)
else:
handle = _winreg.CreateKeyEx(
_winreg.HKEY_LOCAL_MACHINE,
subkey,
0,
_winreg.KEY_ALL_ACCESS
)
_winreg.SetValueEx(handle, vname, 0, _winreg.REG_SZ, vdata)
_winreg.CloseKey(handle)
# time.sleep(15) # delays for 15 seconds
test_success = win_mod_reg.delete_value(
'HKEY_LOCAL_MACHINE',
subkey,
vname
)
self.assertTrue(test_success)
@skipIf(not sys.platform.startswith("win"), "requires Windows OS")
def test_del_key_recursive_user(self):
'''
Test - Create directly key/value pair and Delete recusivly with salt
'''
subkey = 'Software\\SaltStackTest'
vname = UNICODE_TEST_KEY_DEL
vdata = 'I will be deleted recursive'
if PY2:
handle = _winreg.CreateKeyEx(
_winreg.HKEY_CURRENT_USER,
subkey.encode('mbcs'),
0,
_winreg.KEY_ALL_ACCESS
)
_winreg.SetValueEx(
handle,
vname.encode('mbcs'),
0,
_winreg.REG_SZ,
vdata.encode('mbcs')
)
else:
handle = _winreg.CreateKeyEx(
_winreg.HKEY_CURRENT_USER,
subkey,
0,
_winreg.KEY_ALL_ACCESS
)
_winreg.SetValueEx(handle, vname, 0, _winreg.REG_SZ, vdata)
_winreg.CloseKey(handle)
# time.sleep(15) # delays for 15 seconds so you can run regedit & watch it happen
test_success = win_mod_reg.delete_key_recursive('HKEY_CURRENT_USER', subkey)
self.assertTrue(test_success)
@skipIf(not sys.platform.startswith("win"), "requires Windows OS")
@destructiveTest
def test_del_key_recursive_machine(self):
'''
This is a DESTRUCTIVE TEST it creates a new registry entry.
And then destroys the registry entry recusively , however it is completed in its own space
within the registry. We mark this as destructiveTest as it has the potential
to detroy a machine if salt reg code has a large error in it.
'''
subkey = 'Software\\SaltStackTest'
vname = UNICODE_TEST_KEY_DEL
vdata = 'I will be deleted recursive'
if PY2:
handle = _winreg.CreateKeyEx(
_winreg.HKEY_LOCAL_MACHINE,
subkey.encode('mbcs'),
0,
_winreg.KEY_ALL_ACCESS
)
_winreg.SetValueEx(
handle,
vname.encode('mbcs'),
0,
_winreg.REG_SZ,
vdata.encode('mbcs')
)
else:
handle = _winreg.CreateKeyEx(
_winreg.HKEY_LOCAL_MACHINE,
subkey,
0,
_winreg.KEY_ALL_ACCESS
)
_winreg.SetValueEx(handle, vname, 0, _winreg.REG_SZ, vdata)
_winreg.CloseKey(handle)
# time.sleep(15) # delays for 15 seconds so you can run regedit and watch it happen
test_success = win_mod_reg.delete_key_recursive('HKEY_LOCAL_MACHINE', subkey)
self.assertTrue(test_success)
# pylint: disable=W0511
# TODO: Test other hives, other than HKEY_LOCAL_MACHINE and HKEY_CURRENT_USER
if __name__ == '__main__':
from integration import run_tests # pylint: disable=C0413
run_tests(RegWinTestCase, needs_daemon=False)