This class wraps most of the win32api functions for accessing a registry. It will read and write all win32 registry types, and will de/serialize python objects to registry keys when a string or integer representation is not possible.
This is an update of recipe 174627, folding in the corrections listed in the discussion there.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 | # From the recipe at http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/174627
# Corrections and additions have been made
"""Slightly magical Win32api Registry -> Dictionary-like-object wrapper"""
import win32api, win32con, cPickle
class RegistryDict(object):
def __init__(self, keyhandle = win32con.HKEY_LOCAL_MACHINE, keypath = [], flags = None):
"""If flags=None, then it will create the key.. otherwise pass a win32con.KEY_* sam"""
self.keyhandle = None
self.open(keyhandle, keypath, flags)
@staticmethod
def massageIncomingRegistryValue((obj, objtype)):
if objtype == win32con.REG_BINARY and obj[:8]=='PyPickle':
obj = obj[8:]
return cPickle.loads(obj)
elif objtype == win32con.REG_NONE:
return None
elif objtype in (win32con.REG_SZ, win32con.REG_EXPAND_SZ,
win32con.REG_RESOURCE_LIST, win32con.REG_LINK,
win32con.REG_BINARY, win32con.REG_DWORD,
win32con.REG_DWORD_LITTLE_ENDIAN, win32con.REG_DWORD_BIG_ENDIAN,
win32con.REG_MULTI_SZ):
return obj
raise NotImplementedError, "Registry type 0x%08X not supported" % (objtype,)
def __getitem__(self, key):
# is it data?
try:
return self.massageIncomingRegistryValue(win32api.RegQueryValueEx(self.keyhandle, key))
except:
if key == '':
# Special case: this dictionary key means "default value"
raise KeyError, key
pass
# it's probably a registry key then
try:
return RegistryDict(self.keyhandle, key, win32con.KEY_ALL_ACCESS)
except:
pass
# must not be there
raise KeyError, key
def has_key(self, key):
return self.__contains__(key)
def __contains__(self, key):
try:
self.__getitem__(key)
return 1
except KeyError:
return 0
def copy(self):
return dict(self.iteritems())
def __repr__(self):
return repr(self.copy())
def __str__(self):
return self.__repr__()
def __cmp__(self, other):
# Do the objects have the same state?
return self.keyhandle == other.keyhandle
def __hash__(self):
raise TypeError, "RegistryDict objects are unhashable"
def clear(self):
keylist = list(self.iterkeys())
# Two-step to avoid changing the set while iterating over it
for k in keylist:
del self[k]
def iteritems_data(self):
i = 0
# yield data
try:
while 1:
s, obj, objtype = win32api.RegEnumValue(self.keyhandle, i)
yield s, self.massageIncomingRegistryValue((obj, objtype))
i += 1
except:
pass
def iteritems_children(self, access=win32con.KEY_ALL_ACCESS):
i = 0
try:
while 1:
s = win32api.RegEnumKey(self.keyhandle, i)
yield s, RegistryDict(self.keyhandle, [s], access)
i += 1
except:
pass
def iteritems(self, access=win32con.KEY_ALL_ACCESS):
# yield children
for item in self.iteritems_data():
yield item
for item in self.iteritems_children(access):
yield item
def iterkeys_data(self):
for key, value in self.iteritems_data():
yield key
def iterkeys_children(self, access=win32con.KEY_ALL_ACCESS):
for key, value in self.iteritems_children(access):
yield key
def iterkeys(self):
for key, value in self.iteritems():
yield key
def itervalues_data(self):
for key, value in self.iteritems_data():
yield value
def itervalues_children(self, access=win32con.KEY_ALL_ACCESS):
for key, value in self.iteritems_children(access):
yield value
def itervalues(self, access=win32con.KEY_ALL_ACCESS):
for key, value in self.iteritems(access):
yield value
def items(self, access=win32con.KEY_ALL_ACCESS):
return list(self.iteritems())
def keys(self):
return list(self.iterkeys())
def values(self, access=win32con.KEY_ALL_ACCESS):
return list(self.itervalues(access))
def __delitem__(self, key):
# Delete a string value or a subkey, depending on the type
try:
item = self[key]
except:
return # Silently ignore bad keys
itemtype = type(item)
if itemtype is str:
win32api.RegDeleteValue(self.keyhandle, key)
elif isinstance(item, RegistryDict):
# Delete everything in the subkey, then the subkey itself
item.clear()
win32api.RegDeleteKey(self.keyhandle, key)
else:
raise ValueError, "Unknown item type in RegistryDict"
def __len__(self):
return len(self.items())
def __iter__(self):
return self.iterkeys()
def popitem(self):
try:
k, v = self.iteritems().next()
del self[k]
return k, v
except StopIteration:
raise KeyError, "RegistryDict is empty"
def get(self,key,default=None):
try:
return self.__getitem__(key)
except:
return default
def setdefault(self,key,default=None):
try:
return self.__getitem__(key)
except:
self.__setitem__(key)
return default
def update(self,d):
for k,v in d.items():
self.__setitem__(k, v)
def __setitem__(self, item, value):
item = str(item)
pyvalue = type(value)
if pyvalue is dict or isinstance(value, RegistryDict):
d = RegistryDict(self.keyhandle, item)
d.clear()
d.update(value)
return
if pyvalue is str:
valuetype = win32con.REG_SZ
elif pyvalue is int:
valuetype = win32con.REG_DWORD
else:
valuetype = win32con.REG_BINARY
value = 'PyPickle' + cPickle.dumps(value)
win32api.RegSetValueEx(self.keyhandle, item, 0, valuetype, value)
def open(self, keyhandle, keypath, flags = None):
if self.keyhandle:
self.close()
if type(keypath) is str:
keypath = keypath.split('\\')
if flags is None:
for subkey in keypath:
keyhandle = win32api.RegCreateKey(keyhandle, subkey)
else:
for subkey in keypath:
keyhandle = win32api.RegOpenKeyEx(keyhandle, subkey, 0, flags)
self.keyhandle = keyhandle
def close(self):
try:
win32api.RegCloseKey(self.keyhandle)
except:
pass
def __del__(self):
self.close()
|
Here's a couple of examples to show the usage:
import win32con as wc
Get a dict of all the typelibs registered here
typelib = RegistryDict(wc.HKEY_LOCAL_MACHINE,'TypeLib',wc.KEY_ALL_ACCESS)
Get a dict of the ODBC DSNs
DSNs = RegistryDict(wc.HKEY_LOCAL_MACHINE,r'SOFTWARE\ODBC\ODBC.INI',wc.KEY_ALL_ACCESS)
List the DSN names
print [k for k in DSNs.keys()]
Nice, but... ..it still suffers from the same fundamental problem as the orginal - loss of precise type information partly due to the dictionary metaphor and the fact that Python types dont exactly mirror registry types (eg. Python has a string, the registry has REG_SZ, REG_EXPAND_SZ and REG_MULTI_SZ, and you sometimes need to distinguish between them).
So I've made three changes to address this. The interface works exactly as before if used as before, ie.
1) regdict["valuename"]=value
still sets the value to the nearest Python type (if value is a string, then its REG_SZ)
2) regdict["valuename"]
still returns the value in the nearest Python type.
However now you can also do this
3) regdict["valuename"]=value,valuetype
sets the REG_.. type as well as its value
4) regdict[("valuename",)]
returns the the REG_.. type as well as its value (as a value,valuetype) tuple. This is a bit kludgy, but the dictionary metaphore means there isn't really a better way that I can see.
The required changes are (I've listed the three changed methods in full, but there's only a handful of lines actually changed):
(comment continued...)
(...continued from previous comment)
To continue the spirit, I've posted the full changed module as a new recipe and will post the number if/when it's approved.
Deletion bug? At the line marked (*) in:
Or am I missing something?</pre>
Long handling? If you do something like:
regdict["valuename"] = 1l or regdict["valuename"] = 0x12345678
Then "valuename" will appear as a pickled python object, which doesn't seem desirable. That seems to be because "int" is specifically checked for when assigning the variable, but not "long".
So should the line marked with (*) in:
be:
or maybe:
??
Hmmm. Hmm I cant that last int/long change to work. What boils down to call to:
comes back with: ValueError: Could not convert the data to the specified type.
How can it not convert a long of value 1 to a REG_DWORD?