[Tutor] walk registry using _winreg

Albert-Jan Roskam fomcl at yahoo.com
Fri May 31 10:43:03 CEST 2013



----- Original Message -----
> From: eryksun <eryksun at gmail.com>
> To: Albert-Jan Roskam <fomcl at yahoo.com>
> Cc: Dave Angel <davea at davea.name>; "tutor at python.org" <tutor at python.org>
> Sent: Thursday, May 30, 2013 10:05 PM
> Subject: Re: [Tutor] walk registry using _winreg
> 
> On Thu, May 30, 2013 at 10:47 AM, Albert-Jan Roskam <fomcl at yahoo.com> 
> wrote:
>> 
>> def walkRegistry(regkey, keyToSet="file_locations",
>>                   valueToSet="temp_dir",
>>                   HKEY=_winreg.HKEY_CURRENT_USER, verbose=False):
> 
> I suppose you don't need the "sam" option in your case, but in 
> general
> it's needed for 64-bit Windows in order to handle both native and
> WOW64 keys.
> 
> For a WOW64 process, the native 64-bit keys can be read with
> sam=KEY_READ | KEY_WOW64_64KEY. For a 64-bit process, the WOW64 keys
> can be read with sam=KEY_READ | KEY_WOW64_32KEY.
> 
> A WOW64 process will have "PROCESSOR_ARCHITEW6432" defined in 
> os.environ.
 

That's entirely new to me.So every 64-bit windows registry comes in a WOW64 and a native flavour? If one native registry is already a mess, does two registries mean double mess? I will have to read up on this, although I have to admit that the windows registry is not my favourite topic. The walk function might also come in really handy with searching in virtual registries that Symantec virtual software layer creates (talking about mess! Yikes!)

 
>>     aReg = _winreg.OpenKey(HKEY, regkey)
> 
> You should use a "with" statement here instead of depending on the
> garbage collection of the generator frame.


Thank you. Yes, I was staring at the code and this crossed my mind. I found my code too nested (which I considered to be a bad sign) and I didn't use a 'with' statement because it would add yet another nesting level. 

 
>>     try:
>>         while True:
>>             key = _winreg.EnumKey(aReg, i)
>>             i += 1
>>             if key:
>>                 new_regkey = os.path.join(regkey, key)
> 
> There's too much code here under the banner of one "try" suite.
> OpenKey and QueryValueEx in the subsequent statements may raise a
> WindowsError for various reasons.
> 
> Also, as you're currently doing things it leaves several open handles
> as you recursively create generators. It's likely not an issue (the
> registry isn't deeply nested), but in general I prefer to close a
> resource as immediately as is possible.
> 
> I'd enumerate the subkeys in a list and only yield a key/value match
> for the current regkey (that's basically how os.walk traverses the
> file system). This can match on the initial key. If you don't want
> that, it can be worked around (e.g. a flag, or a helper function), but
> I don't think the additional complexity is worth it.
> 
>     import os
>     import _winreg
> 
>     def walkRegistry(regkey,
>                     keyToSet="file_locations",
>                     valueToSet="temp_dir",
>                     HKEY=_winreg.HKEY_CURRENT_USER,
>                     sam=_winreg.KEY_READ,
>                     onerror=None,


The onerror parameter is nice (just like in os.walk). Cool way to handle errors.


>                     verbose=False):
> 
>         try:
>             aReg = _winreg.OpenKey(HKEY, regkey)
>         except WindowsError as e:
>             if onerror is not None:
>                 onerror(e)
>             return
> 
>         i = 0
>         subkeys = []
>         with aReg:


I always forget that "with" statements also work without using "as" (ie, with _winreg.OpenKey(HKEY, regkey) as aReg).

Another remark that happens to be also about "as": is "except WindowsError as e:" a more modern version of "except WindowsError, e:"? Comparable to the old-fashioned "raise SomeError, 'error'"  and the new "raise SomeError('error')".  
. 

>             while True:
>                 try:
>                     subkeys.append(_winreg.EnumKey(aReg, i))
>                 except WindowsError:
>                     break
>                 i += 1
>             # check the key name; not the key path
>             if os.path.basename(regkey) == keyToSet:
>                 if verbose:
>                     print "---> FOUND KEY:", regkey
>                 try:
>                     data = _winreg.QueryValueEx(aReg, valueToSet)[0]
>                 except WindowsError:  # value not found
>                     pass
>                 else:


Thanks for reminding me of the "else" clause in the try-except suite. I knew it existed, but I never use it. For others who are reading along, the links below are useful. Basically, it boils down to: (1) keep the "try" clause as empty as possible (2) the "else" clause is there to prevent you from catching the same type of exception, but with a different cause.
http://stackoverflow.com/questions/855759/python-try-else
http://docs.python.org/2/tutorial/errors.html: "The use of the else clause is better than adding additional code to the try clause because it avoids accidentally catching an exception that wasn’t raised by the code being protected by the try ... except statement"

 
>                     if verbose:
>                         print "---> FOUND KEY,VALUE PAIR"
>                     yield regkey, valueToSet, data
> 
>         for key in subkeys:
>             new_regkey = os.path.join(regkey, key)
>             for item in walkRegistry(
>                     new_regkey, keyToSet, valueToSet,
>                     HKEY, sam, onerror, verbose):
>                 yield item
> 
> 
> Minimally tested (sorry):


You all have been of enormous help, thank you!! Code reviews are a great way to find errors, make code more readable, make code more efficient (in that order) and learn new tricks
I wrote a simple doctest for the code. Perhaps I should post this somewhere.
    r"""
    >>> regkey = u"Software\\Microsoft\\Windows\\CurrentVersion\\Explorer"
    >>> args = regkey, u"Shell Folders", u"Cookies"
    >>> regdata = [(key, val, data) for key, val, data in walkRegistry(*args)]
    >>> print regdata  # doctest: +ELLIPSIS, +NORMALIZE_WHITESPACE
    [(u'Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\Shell Folders',
    u'Cookies', u'...\\Microsoft\\Windows\\Cookies')]
    """
....
if __name__ == "__main__":
    import doctest
    doctest.testmod()
    regkey = u"Software\\Microsoft\\Windows\\CurrentVersion\\Explorer"
    args = regkey, u"Shell Folders", u"Cookies"
    regdata = [(key, val, data) for key, val, data in walkRegistry(*args)]
    print regdata


> 
>     >>> HKEY = _winreg.HKEY_LOCAL_MACHINE
>     >>> regkey = r'Software\Python\PythonCore\2.7'
> 
>     >>> res = list(
>     ... walkRegistry(regkey, 'PythonPath', '', HKEY, 
> verbose=True))
> 
>     ---> FOUND KEY: Software\Python\PythonCore\2.7\PythonPath
>     ---> FOUND KEY,VALUE PAIR
> 
>     >>> res[0][2]
>     
> u'C:\\Python27\\Lib;C:\\Python27\\DLLs;C:\\Python27\\Lib\\lib-tk'
> 


More information about the Tutor mailing list