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

## nsIVariant

There is (almost) full support for nsIVariant. Any nsIVariant parameters will automatically be translated to and from regular Python objects giving, in effect, a multi-type parameter. This should be automatic, so there is not much else to say! Note that if you really want, you can create and pass your own nsIVariant object instead of a regular Python object, thereby allowing explicit control over the type of variant created.

## 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 nsISupportsCString 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 nsISupportsCString or nsISupportsString 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 nsISupportsCString and nsISupportsString, 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 nsISupportsCString 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, since this interface is not "scriptable", you should avoided using it in interfaces you design.

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.