|
Python XPCOM Advanced Topics
This document contains a series of tidbits that don't fit
anywhere else. As the Python XPCOM Package documentation matures, most of
these topics will have another home.
XPCOM Services
An XPCOM service is simply a singleton registered by name. Python has
full support for both using and implementing XPCOM services. To use a
service, use xpcom.components.services just like the JavaScript
counterpart. There is nothing special about implementing a service in
Python; see the standard XPCOM documentation on services for more information.
nsISupports Primitives
There is a set of interfaces described in nsISupportsPrimitives.idl, which I
term collectively the nsISupports Primitives Interfaces. These
are a set of interfaces a component can support to allow automatic conversion to
and from many basic types. For example, an interface can define that it
supports the nsISupportsString interface, and this could be used by any
program that wishes to get a string representation of the object. If an
interface wishes to expose itself as a "boolean value", it may choose
to support the nsISupportsPRBool interface.
When you call an XPCOM object (i.e., you have an XPCOM interface you are
calling), you can use
the builtin functions str(), int(), long() etc., on the
object. In the
case of str(), if the object does not support the nsISupportsString
or nsISupportsWString interfaces, the default string str() for the
object will be returned (i.e., what is normally returned for most XPCOM objects -
support for these interface is not very common!). In the case of the numeric functions, a ValueError
exception will be raised if the objects do not support any interface that can be
used for the conversion. ValueError is used instead of TypeError,
as the type itself (i.e., an XPCOM object) can sometimes be used in this context -
hence it is the specific value of the object that is the problem.
The use of repr() on an XPCOM interface object prevents support
attempts for these interfaces, and allows you to see the
"real" object, rather than what the object wants you to see!
When you implement an XPCOM object, you have two choices for implementation
of these interfaces:
- You can explicitly handle these interfaces like any other interface.
In this case, you have full control. However, if you
implement only one of these standard interfaces, then you are only
overriding the default behavior for that specific interface - all other
interfaces not explicitly listed in your class will still get the behavior
described below.
- If your class does not define support for these interfaces, the framework
will use standard Python class semantics to implement them - i.e., if your
class provides a __str__ method, it will be used to implement nsISupportsString
and nsISupportsWString, if you provide __int__, __long__,
__float__ etc., methods, they will be used to implement the numeric
interfaces. If your class defines no such special methods, then the
QueryInterface() for those interfaces fails (rather than the QI succeeding
and the operation to fetch the data failing).
This allows for an interesting feature that would not normally be
possible. Consider Python code that does a str() on an XPCOM
interface, and where the XPCOM interface itself is implemented in Python and
provides a __str__ method. The str() on the original
interface queries for the nsISupportsString interface. The
Python implemented object responds to this interface and delegates to the __str__
method. At the end of all this, str() returns the same result
as if the objects were native Python objects with no XPCOM layer in between.
Enumerators
The primary enumerator used by XPCOM is nsISimpleEnumerator.
Although the Python XPCOM package has full support for nsIEnumerator, you should avoid using it since it is not a "scriptable" interface.
When you use nsISimpleEnumerator from Python, the following enhancements
are available:
- The GetNext() method takes an optional IID as a parameter. If
this is specified, the returned object will be of this interface. This
prevents the manual QueryInterface() generally required from other
languages.
- There is a FetchBlock(num, [iid]) method, which fetches the
specified number of elements in one operation and returns a Python
list. This can be useful for large enumerator sets, so the loop
iterating the elements runs at full C++ speed.
nsIEnumerator has similar enhancements.
When implementing a Python XPCOM object, the Python class xpcom.server.enumerator.SimpleEnumerator()
can be used. You can pass a standard Python sequence (list, etc), and it
will be correctly wrapped in an nsISimpleEnumerator interface.
Files
The Python XPCOM package provides an xpcom.file module. This implements
a Python-like file object on top of the XPCOM/Mozilla stream interfaces.
When run from within the Mozilla environment, this allows you to open almost any
URL supported by Mozilla (including "chrome://" etc.,).
See this module for more information, including test code.
XPCOM Object Identity
XPCOM has defined rules for object identity and for how objects must behave
in their QueryInterface() implementations. The Python XPCOM framework
manages this for you; your code can return new Python instances etc., when
responding to new interfaces, and the framework itself will ensure the XPCOM
semantics are followed. Critically, the framework provides no mechanism
for breaking these rules.
Policies
The Python XPCOM framework has the concept of "policies" that
define how XPCOM semantics are mapped to Python objects. It is the policy
that implements delegation of QueryInterface(), translates property
references into direct property references, and failing that, "get_name"
and "set_name" calls, decides how to handle exceptions in the
component, and so on.
The default policy is very flexible and suitable for most purposes.
Indeed, the Komodo project has never had to implement a custom policy.
However, you should be aware the feature exists should you wish to do some
bizarre things, such as using Python as a bridge between XPCOM and some other
component technology.
|