[Python-Dev] Winreg update
Paul Prescod
paul@prescod.net
Fri, 11 Aug 2000 08:25:27 -0500
I am in transit so I don't have time for a lot of back and forth email
relating to winreg. It also seems that there are a lot of people (let's
call them "back seat coders") who have vague ideas of what they want but
don't want to spend a bunch of time in a long discussion about registry
arcana. Therefore I am endevouring to make it as easy and fast to
contribute to the discussion as possible.
I'm doing this through a Python Module Proposal format. This can also
serve as the basis of documentation.
This is really easy so I want
some real feedback this time. Distutils people, this means you! Mark! I
would love to hear Bill Tutt, Greg Stein and anyone else who claims some
knowledge of Windows!
If you're one of the people who has asked for winreg in the core then
you should respond. It isn't (IMO) sufficient to put in a hacky API to
make your life easier. You need to give something to get something. You
want windows registry support in the core -- fine, let's do it properly.
Even people with a minimal understanding of the registry should be able
to contribute: the registry isn't rocket surgery. I'll include a short
primer in this email.
All you need to do is read this email and comment on whether you agree
with the overall principle and then give your opinion on fifteen
possibly controversial issues. The "overall principle" is to steal
shamelessly from Microsoft's new C#/VB/OLE/Active-X/CRL API instead of
innovating for Python. That allows us to avoid starting the debate from
scratch. It also eliminates the feature that Mark complained about
(which was a Python-specific innovation).
The fifteen issues are mostly extensions to the API to make it easier
(convenience extensions) or more powerful (completeness extensions).
Many of them are binary: "do this, don't do that." Others are choices:
e.g. "Use tuples", "Use lists", "Use an instance".
I will try to make sense of the various responses. Some issues will have
strong consensus and I'll close those quickly. Others will require more
(hopefully not much!) discussion.
Windows Registry Primer:
========================
There are things called "keys". They aren't like Python keys so don't
think of them that way. Keys have a list of subkeys indexed by name.
Keys also have a list of "values". Values have names. Every value has a
type. In some type-definition syntax:
key is (name: string,
subkeys: (string : key),
values: (string : value ))
value is ( name: string,
type: enumeration,
data: (depends on enumeration) )
That's the basic model. There are various helper facilities provided by
the APIs, but really, the model is as above.
=========================================================================
Python Module Proposal
Title: Windows registry
Version: $Revision: 1.0$
Owner: paul@prescod.net (Paul Prescod)
Python-Version: 2.0
Status: Incomplete
Overview
It is convenient for Windows users to know that a Python module to
access the registry is always available whenever Python is installed
on Windows. This is especially useful for installation programs.
There is a Windows registry module from the win32 extensions to
Python. It is based directly on the original Microsoft APIs. This
means that there are many backwards compatibility hacks, "reserved"
parameters and other legacy features that are not interesting to
most Python programmers. Microsoft is moving to a higher level API
for languages other than C, as part of Microsoft's Common Runtime
Library (CRL) initiative. This newer, higher level API serves as
the basis for the module described herein.
This higher level API would be implemented in Python and based upon
the low-level API. They would not be in competition: a user would
choose based on their preferences and needs.
Module Exports
These are taken directly from the Common Runtime Library:
ClassesRoot The Windows Registry base key HKEY_CLASSES_ROOT.
CurrentConfig The Windows Registry base key HKEY_CURRENT_CONFIG.
CurrentUser The Windows Registry base key HKEY_CURRENT_USER.
LocalMachine The Windows Registry base key HKEY_LOCAL_MACHINE.
CurrentUser The Windows Registry base key HKEY_CURRENT_USER.
DynData The Windows Registry base key HKEY_DYN_DATA.
PerformanceData The Windows Registry base key HKEY_PERFORMANCE_DATA.
Users The Windows Registry base key HKEY_USERS.
RegistryKey Registry key class (important class in module)
RegistryKey class Data Members
These are taken directly from the Common Runtime Library:
Name Retrieves the name of the key.
[Issue: full path or just name within parent?]
SubKeyCount Retrieves the count of subkeys.
ValueCount Retrieves the count of values in the key.
RegistryKey Methods
These are taken directly from the Common Runtime Library:
Close()
Closes this key and flushes it to disk if the contents have
been modified.
CreateSubKey( subkeyname )
Creates a new subkey or opens an existing subkey.
[Issue: SubKey_full_path]: Should it be possible to create a subkey
deeply:
>>> LocalMachine.CreateSubKey( r"foo\bar\baz" )
Presumably the result of this issue would also apply to every
other method that takes a subkey parameter.
It is not clear what the CRL API says yet (Mark?). If it says
"yes" then we would follow it of course. If it says "no" then
we could still consider the feature as an extension.
[Yes] allow subkey parameters to be full paths
[No] require them to be a single alphanumeric name, no slashes
DeleteSubKey( subkeyname )
Deletes the specified subkey. To delete subkeys and all their
children (recursively), use DeleteSubKeyTree.
DeleteSubKeyTree( subkeyname )
Recursively deletes a subkey and any child subkeys.
DeleteValue( valuename )
Deletes the specified value from this key.
__cmp__( other )
Determines whether the specified key is the same key as the
current key.
GetSubKeyNames()
Retrieves an array of strings containing all the subkey names.
GetValue( valuename )
Retrieves the specified value.
Registry types are converted according to the following table:
REG_NONE: None
REG_SZ: UnicodeType
REG_MULTI_SZ: [UnicodeType, UnicodeType, ...]
REG_DWORD: IntegerType
REG_DWORD_LITTLE_ENDIAN: IntegerType
REG_DWORD_BIG_ENDIAN: IntegerType
REG_EXPAND_SZ: Same as REG_SZ
REG_RESOURCE_LIST: Same as REG_BINARY
REG_FULL_RESOURCE_DESCRIPTOR: Same as REG_BINARY
REG_RESOURCE_REQUIREMENTS_LIST: Same as REG_BINARY
REG_LINK: Same as REG_BINARY??? [Issue: more info needed!]
REG_BINARY: StringType or array.array( 'c' )
[Issue: REG_BINARY Representation]:
How should binary data be represented as Python data?
[String] The win32 module uses "string".
[Array] I propose that an array of bytes would be better.
One benefit of "binary" is that allows SetValue to detect
string data as REG_SZ and array.array('c') as REG_BINARY
[Issue: Type getting method]
Should there be a companion method called GetType that fetches
the type of a registry value? Otherwise client code would not
be able to distinguish between (e.g.) REG_SZ and
REG_SZ_BINARY.
[Yes] Add GetType( string )
[No] Do not add GetType
GetValueNames()
Retrieves a list of strings containing all the value names.
OpenRemoteBaseKey( machinename, name )
Opens a new RegistryKey that represents the requested key on a
foreign machine.
OpenSubKey( subkeyname )
Retrieves a subkey.
SetValue( keyname, value )
Sets the specified value
Types are automatically mapped according to the following
algorithm:
None: REG_NONE
String: REG_SZ
UnicodeType: REG_SZ
[UnicodeType, UnicodeType, ...]: REG_MULTI_SZ
[StringType, StringType, ...]: REG_MULTI_SZ
IntegerType: REG_DWORD
array.array('c'): REG_BINARY
[Issue: OptionalTypeParameter]
Should there be an optional parameter that allows you to
specify the type explicitly? Presume that the types are
constants in the winreg modules (perhaps strings or
integers).
[Yes] Allow other types to be specified
[No] People who want more control should use the underlying
win32 module.
Proposed Extensions
The API above is a direct transliteration of the .NET API. It is
somewhat underpowered in some senses and also is not entirely
Pythonic. It is a good start as a basis for consensus, however,
and these proposed extensions can be voted up or down individually.
Two extensions are just the convenience functions (OpenRemoteKey
and the top-level functions). Other extensions attempt to extend
the API to support ALL features of the underlying API so that users
never have to switch from one API to another to get a particular
feature.
Convenience Extension: OpenRemoteKey
It is not clear to me why Microsoft restricts remote key opening
to base keys. Why does it not allow a full path like this:
>>> winreg.OpenRemoteKey( "machinename",
r"HKEY_LOCAL_MACHINE\SOFTWARE\Python" )
[Issue: Add_OpenRemoteKey]:
[Yes] add RemoteKey
[No] do not add?
[Issue: Remove_OpenRemoteBaseKey]
[Remove] It's redundant!
[Retain] For backwards compatibility
Convenience Extension: Top-level Functions
A huge number of registry-manipulating programs treat the
registry namespace as "flat" and go directly to the interesting
registry key. These top-level functions allow the Python user
to skip all of the OO key object and get directly to what
they want:
key=OpenKey( keypath, machinename=None )
key=CreateKey( keypath, machinename=None )
DeleteKey( keypath, machinename=None )
val=GetValue( keypath, valname, machinename=None )
SetValue( keypath, valname, valdata, machinename=None )
[Yes] Add these functions
[No] Do not add
[Variant] I like the idea but would change the function
signatures
Completeness Extension: Type names
If the type extensions are added to SetValue and GetValue then
we need to decide how to represent types. It is fairly clear
that they should be represented as constants in the module. The
names of those constants could be the cryptic (but standard)
Microsoft names or more descriptive, conventional names.
Microsoft Names:
REG_NONE
REG_SZ
REG_EXPAND_SZ
REG_BINARY
REG_DWORD
REG_DWORD_LITTLE_ENDIAN
REG_DWORD_BIG_ENDIAN
REG_LINK
REG_MULTI_SZ
REG_RESOURCE_LIST
REG_FULL_RESOURCE_DESCRIPTOR
REG_RESOURCE_REQUIREMENTS_LIST
Proposed Descriptive Names:
NONE
STRING
EXPANDABLE_TEMPLATE_STRING
BINARY_DATA
INTEGER
LITTLE_ENDIAN_INTEGER
BIG_ENDIAN_INTEGER
LINK
STRING_LIST
RESOURCE_LIST
FULL_RESOURCE_DESCRIPTOR
RESOURCE_REQUIREMENTS_LIST
We could also allow both. One set would be aliases for the
other.
[Issue: TypeNames]:
[MS Names]: Use the Microsoft names
[Descriptive Names]: Use the more descriptive names
[Both]: Use both
Completeness Extension: Type representation
No matter what the types are called, they must have values.
The simplest thing would be to use the integers provided by the
Microsoft header files. Unfortunately integers are not at all
self-describing so getting from the integer value to something
human readable requires some sort of switch statement or mapping.
An alternative is to use strings and map them internally to the
Microsoft integer constants.
A third option is to use object instances. These instances would
be useful for introspection and would have the following
attributes:
msname (e.g. REG_SZ)
friendlyname (e.g. String)
msinteger (e.g. 6 )
They would have only the following method:
def __repr__( self ):
"Return a useful representation of the type object"
return "<RegType %d: %s %s>" % \
(self.msinteger, self.msname, self.friendlyname )
A final option is a tuple with the three attributes described
above.
[Issue: Type_Representation]:
[Integers]: Use Microsoft integers
[Strings]: Use string names
[Instances]: Use object instances with three introspective
attributes
[Tuples]: Use 3-tuples
Completeness Extension: Type Namespace
Should the types be declared in the top level of the module
(and thus show up in a "dir" or "from winreg import *") or
should they live in their own dictionary, perhaps called
"types" or "regtypes". They could also be attributes of some
instance.
[Issue: Type_Namespace]:
[Module]: winreg.REG_SZ
[Dictionary]: winreg.types["REG_SZ"]
[Instance]: winreg.types["REG_SZ"]
Completeness Extension: Saving/Loading Keys
The underlying win32 registry API allows the loading and saving
of keys to filenames. Therefore these could be implemented
easily as methods:
def save( self, filename ):
"Save a key to a filename"
_winreg.SaveKey( self.keyobj, filename )
def load( self, subkey, filename ):
"Load a key from a filename"
return _winreg.RegLoadKey( self.handle, subkey,
filename )
>>> key.OpenSubKey("Python").save( "Python.reg" )
>>> key.load( "Python", "Python.reg" )
[Issue: Save_Load_Keys]
[Yes] Support the saving and loading of keys
[No] Do not add these methods
Completeness Extension: Security Access Flags
The underlying win32 registry API allows security flags to be
applied to the OpenKey method. The flags are:
"KEY_ALL_ACCESS"
"KEY_CREATE_LINK"
"KEY_CREATE_SUB_KEY"
"KEY_ENUMERATE_SUB_KEYS"
"KEY_EXECUTE"
"KEY_NOTIFY"
"KEY_QUERY_VALUE"
"KEY_READ"
"KEY_SET_VALUE"
These are not documented in the underlying API but should be for
this API. This documentation would be derived from the Microsoft
documentation. They would be represented as integer or string
constants in the Python API and used something like this:
key=key.OpenKey( subkeyname, winreg.KEY_READ )
[Issue: Security_Access_Flags]
[Yes] Allow the specification of security access flags.
[No] Do not allow this specification.
[Issue: Security_Access_Flags_Representation]
[Integer] Use the Microsoft integers
[String] Use string values
[Tuples] Use (string, integer) tuples
[Instances] Use instances with "name", "msinteger"
attributes
[Issue: Security_Access_Flags_Location]
[Top-Level] winreg.KEY_READ
[Dictionary] winreg.flags["KEY_READ"]
[Instance] winreg.flags.KEY_READ
Completeness Extension: Flush
The underlying win32 registry API has a flush method for keys.
The documentation is as follows:
"""Writes all the attributes of a key to the registry.
It is not necessary to call RegFlushKey to change a key.
Registry changes are flushed to disk by the registry using
its lazy flusher. Registry changes are also flushed to
disk at system shutdown. Unlike \function{CloseKey()}, the
\function{FlushKey()} method returns only when all the data
has been written to the registry. An application should
only call \function{FlushKey()} if it requires absolute
certainty that registry changes are on disk."""
If all completeness extensions are implemented, the author believes
that this API will be as complete as the underlying API so
programmers can choose which to use based on familiarity rather
than feature-completeness.
--
Paul Prescod - Not encumbered by corporate consensus
"I don't want you to describe to me -- not ever -- what you were doing
to that poor boy to make him sound like that; but if you ever do it
again, please cover his mouth with your hand," Grandmother said.
-- John Irving, "A Prayer for Owen Meany"