Welcome, guest | Sign In | My Account | Store | Cart

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.

Python, 202 lines
  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
"""Slightly magical Win32api Registry -> Dictionary-like-object wrapper"""

from __future__ import generators
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"""
        keyhandle = None
        self.open(keyhandle, keypath, flags)

    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,)
    massageIncomingRegistryValue = staticmethod(massageIncomingRegistryValue)

    def __getitem__(self, item):
        item = str(item)
        
        # is it data?
        try:
            return self.massageIncomingRegistryValue(win32api.RegQueryValueEx(self.keyhandle, item))
        except:
            pass

        # it's probably a key then
        try:
            return RegistryDict(self.keyhandle, item, win32con.KEY_ALL_ACCESS)
        except:
            pass

        # must not be there
        raise KeyError, item
    
    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):
        return cmp(self.copy(), other)

    def __hash__(self):
        raise TypeError, "RegistryDict objects are unhashable"
  
    def clear(self):
        for k in self.iterkeys():
            del self[k]
    
    def iteritems_data(self):
        i = 0
        # yield data
        try:
            while 1:
                s, obj, objtype = win32api.RegEnumValue(self.keyhandle, i)
                yield s, massageRegistryValue((obj, objtype))
                i += 1
        except:
            pass

    def iteritems_children(self, access=win32con.KEY_ALL_ACCESS):
        i = 0
        try:
            while 1:
                s, obj, objtype = 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, item):
        win32api.RegDeleteValue(self.keyhandle, str(item))
  
    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()

A developer would choose this recipe to make working with the win32 registry pythonic. It's also very useful when implementing a cross-platform solution to storing preferences. For example, you could have a proxy class use ~/.YourCompany.YourSoftware (and a pickled dict) for unix, HKLM\Software\YourCompany\YourSoftware registry keys (using this class) in windows, and ~/Library/Preferences/YourCompany.YourSoftware.plist (and a plist-ed/pickled dict) for OS X.

13 comments

Harald Armin massa 19 years, 8 months ago  # | flag

self. missing. 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 # THIS should be this way, should it not? self.open(keyhandle, keypath, flags)

otherwise self.open leads to "... has no attribut "keyhandle")

Don Dwiggins 19 years, 1 month ago  # | flag

Updates and fixes. I've had occasion to use this class, and have found it quite nice. There are a few changes I've had to make, though:

The previous comment's correction, adding "self." to the first line of __init__ to make keyhandle an ivar, is correct.

Changed the definition of __cmp__ to the following:

# Do the objects have the same state?
return self.keyhandle == other.keyhandle

Changed the definition of clear to the following:

keylist = list(self.iterkeys())
# Two-step to avoid changing the set while iterating over it
for k in keylist:
    del self[k]

In iteritems_data, the call to "massage..." should be:

yield s, self.massageIncomingRegistryValue((obj, objtype))

In iteritems_children, changed the RegEnumKey line to read:

s = win32api.RegEnumKey(self.keyhandle, i)

The __delitem__ method only deletes string values. I changed it to the following:

# 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"

Note the use of .clear() to delete the whole tree under the subkey. A more cautious approach would be to require the client code to manage that.

In the "open" method, the "else" clause is one level too deep. The "else" should be paired with "if flags is None:"

If desired, I can put a patch or the whole file somewhere around here.

Don Dwiggins 19 years, 1 month ago  # | flag

Oops! one more fix. I just ran across another subtle bug: the dictionary key '' (the empty string) must mean the "default value" of a registry key, but it was returning a copy of "self" if there was no default value. The following updated __getitem__ fixes this:

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
William McVey 18 years, 10 months ago  # | flag

Requests. This looks like a very cool recipe. It would help though to have the updated/fixed version of this recipe posted in it's entirety. Cutting and pasting the fixes from the comments is somewhat error prone. It'd also be nice if some simple test cases were constructed to act as a demonstration of the usage of this module.

Rob Walker 18 years, 3 months ago  # | flag

I second that. I agree with the previous post. Before or after applying the mentioned bug fixes, I've never gotten this to work. Without clear examples and tests in a main function like many other recipes, I wouldn't be sure if I were using it properly anyway. This looks like a very useful and promising recipe, but it definitely could be improved with unit tests and some refactoring.

Michael Babcock 18 years ago  # | flag

Fixed script, downloadable. You can download the entire script with the fixes above from:

http://mikebabcock.ca/code/registrydict.py

You're welcome :)

PS, I'll update this with any other patches/fixes I notice from here. I made one change myself as well, by removing an unnecessary temporary variable in one of the other patches.

Ross McKerchar 17 years, 10 months ago  # | flag

Still not quite right. This script at http://mikebabcock.ca/code/registrydict.py also needs a wee fix (I've emailed the author about it):

Line26: def __getitem__(self, item):

should be (I think):

def __getitem__(self, key)

Now, I wish someone could show me how it works!

-ross

Ross McKerchar 17 years, 10 months ago  # | flag

Save unicode as REG_SZ. May be worth putting

        if isinstance(value,unicode): #unicode...
            value = value.encode()
<pre>At line 186 in __setitem__ so that unicode strings get saved as a REG_SZ type (i.e. a string). Currently unicode string get stored as binary (which python handles transparently, but other apps might take offense...)

There's probably a neater way of doing this, I'm stilling learning the way of pythonic coding...

</pre>

Ross McKerchar 17 years, 10 months ago  # | flag

__delitem__ rewrite. I've rewritten __delitem__ as it just plain didn't work...

def __delitem__(self, item):
    # Delete a string value or a subkey, depending on the type
    key = self[item]
    keytype = type(key)
    if keytype is RegistryDict:
        # Delete everything in the subkey, then the subkey itself
        key.clear()
        win32api.RegDeleteKey(self.keyhandle, item)
    else:
        win32api.RegDeleteValue(self.keyhandle, item)

It's got slightly different behaviour: the old version looked like it was attempting to silently fail if you deleted a key that didn't exist.

This version will raise a keyerror, which is more consistent with the behaviour of standard dictionaries, i.e.

del {}["foo"]

Will raise a keyerror, not fail silently.

Michael Babcock 17 years, 10 months ago  # | flag

Updated my hosted copy. http://mikebabcock.ca/code/registrydict.py has been updated appropriately as per the updates above.

peekaa 17 years, 9 months ago  # | flag

doesnt work. I try to run this script with no success. Is this right?: Should be printed subkeys this way? Neither of these is working :-(

print RegistryDict()
print RegistryDict(flags=win32con.KEY_ALL_ACCESS)
print RegistryDict(keypath="HARDWARE", flags=win32con.KEY_ALL_ACCESS)

Moreover, here: http://mikebabcock.ca/code/registrydict.py is just part of updated script, so I am not sure, if I collect the right script of last version.

Thank you very much

Don Dwiggins 16 years ago  # | flag

Updated version available in a new recipe. I've just submitted a new recipe, number 551761, with all the corrections applied; it's awaiting approval.

Rahul Baiswar 12 years, 9 months ago  # | flag

what will this call return, if the keyPath in the below method call doesnt exist, then what would be return value of function call? rd = RegistryDict(win32con.HKEY_LOCAL_MACHINE, KeyPath)

so basically i first want to find if key path exists. if yes, then want to find the value of some key at this key path.

thanks, Rahul