Python XPCOM Package Tutorial

This is a quick introduction to the Python XPCOM Package. We assume that you have a good understanding of Python and XPCOM, and have experience both using and implementing XPCOM objects in some other language (e.g., C++ or JavaScript). We do not attempt to provide a tutorial to XPCOM or Python itself, only to using Python and XPCOM.

This tutorial contains the following sections:

For anything not covered here, try the advanced documentation, and if that fails, use the source, Luke!

Using XPCOM object and interfaces.

The techniques for using XPCOM in Python have been borrowed from JavaScript - thus, the model described here should be quite familiar to existing JavaScript XPCOM programmers.

xpcom.components module

When using an XPCOM object, the primary module used is the xpcom.components module.  Using this module, you can get a Python object that supports any scriptable XPCOM interface. Once you have this Python object, you can simply call XPCOM methods on the object, as normal.

The xpcom.components module defines the following public members:

Name Description
classes A mapping (dictionary-like object) used to get XPCOM "classes".  These are indexed by XPCOM contract ID, just like the JavaScript object of the same name.  

Example:

cls = components.classes["@mozilla.org/sample;1"]
ob = cls.createInstance() # Now have an nsISupports
interfaces An object that exposes all XPCOM interface IDs (IIDs).  Like the JavaScript object of the same name, this object uses "dot" notation, as demonstrated below.

Example:

ob = cls.createInstance(components.interfaces.nsISample) 
# Now have an nsISample

For many people, this is all you need to know. Consider the Mozilla Sample Component.  The Mozilla Sample Component has a contract ID of @mozilla.org/sample;1, and implements the nsISample interface.

Thus, a complete Python program that uses this component is shown below.

from xpcom import components
cls = components.classes["@mozilla.org/sample;1"]
ob = cls.createInstance() # no need to specify an IID for most components
# nsISample defines a "value" property - let's use it!
ob.value = "new value"
if ob.value != "new value":
    print "Eeek - what happened?"

And that is it - a complete Python program that uses XPCOM.

Implementing XPCOM Objects and Interfaces.

Implementing XPCOM objects is almost as simple as using them. The basic strategy is this:

  1. Create a standard Python source file, with a standard Python class.
  2. Add some special attributes to your class for use by the Python XPCOM framework. This controls the XPCOM behavior of your object.
  3. Implement the XPCOM properties and methods of your classes as normal.
  4. Put the Python source file in the Mozilla components directory.
  5. Run regxpcom.

Your component is now ready to be used.

Attributes

There are two classes of attributes: those used at runtime to define the object behavior and those used at registration time to control object registration.  Not all objects require registration, thus not all Python XPCOM objects will have registration-related attributes.

Attribute Description
_com_interfaces_ The interface IDs (IIDs) supported by the component.  For simplicity, this may be either a single IID, or a list of IIDs.  There is no need to specify base interfaces, as all parent interfaces are automatically supported. Thus, it is never necessary to nominate nsISupports in the list of interfaces.

This attribute is required. Objects without such an attribute are deemed unsuitable for use as a XPCOM object.

_reg_contractid_ The contract ID of the component.  Required if the component requires registration (i.e., exists in the components directory).
_reg_clsid_ The Class ID (CLSID) of the component, as a string in the standard "{XXX-XXX-XXX-XXX}" format. Required if the component requires registration (i.e., exists in the components directory).
_reg_registrar_ Nominates a function that is called at registration time. The default is for no extra function to be called. This can be useful if a component has special registration requirements and needs to hook into the registration process.
_reg_desc_ The description of the XPCOM object. This may be used by browsers or other such objects.  If not specified, the contract ID is used.

Properties

A Python class can support XPCOM properties in one of two ways.  Either a standard Python property of the same name can exist - our sample component demonstrates this with the boolean_value property.  Alternatively, the class can provide the get_propertyName(self) and set_propertyName(self, value) functions (with propertyName changed to the appropriate value for the property), and these functions will be called instead.

Example:  The Python XPCOM Test Component

As an example, examine the Python XPCOM Test Component.  This code can be found in py_test_component.py.

from xpcom import components

class PythonTestComponent:
    _com_interfaces_ = components.interfaces.nsIPythonTestInterface
    _reg_clsid_ = "{7EE4BDC6-CB53-42c1-A9E4-616B8E012ABA}"
    _reg_contractid_ = "Python.TestComponent"
    def __init__(self):
        self.boolean_value = 1
        ...
    def do_boolean(self, p1, p2):
        ret = p1 ^ p2
        return ret, not ret, ret
...

Note: This component only specifies the mandatory attributes - _com_interfaces, _reg_clsid_ and _reg_contractid_.

This sample code demonstrates supporting the boolean_value attribute, supported implicitly, as it is defined in the IDL and exists as a real Python attribute of that name, and a method called do_boolean.

Tip: The xpcom/xpt.py Script

The xpcom/xpt.py script is a useful script that can generate the skeleton of a class for any XPCOM interface.  Just specify the interface name on the command-line, and paste the output into your source file.

This is the output of running this program over the nsISample interface (i.e., assuming we wanted to implement a component that supported this interface):

class nsISample:
    _com_interfaces_ = xpcom.components.interfaces.nsISample
    # If this object needs to be registered, the following 2 are also needed.
    # _reg_clsid_ = {a new clsid generated for this object}
    # _reg_contractid_ = "The.Object.Name"

    def get_value( self ):
        # Result: string
        pass
    def set_value( self, param0 ):
        # Result: void - None
        # In: param0: string
        pass
    def writeValue( self, param0 ):
        # Result: void - None
        # In: param0: string
        pass
    def poke( self, param0 ):
        # Result: void - None
        # In: param0: string
        pass

Note: The types of the parameters and the function itself are included in the comments. You need to implement the functions themselves.  Another advantage of this script is that the hidden parameters are handled for you; the comments indicate when parameters have been hidden.

Parameters and Types

This section briefly describes the XPCOM type support in Python.

All XPCOM interfaces define parameters of a specific type.  There is currently no concept of a variant, or union of all types. Thus, the conversion rules are very straightforward, and generally surprise free: for any given XPCOM method, there is only one possible type for a given parameter.

Type Conversion Rules:

Interface Flattening

Most people can ignore this information - Python XPCOM objects just work.  However, if you are familiar with xpcom from C++ and the concept of QueryInterface, you may like to read this.

Most components support the concept of "interface flattening".  Such objects can report the interfaces they support, allowing languages such as Python and Javascript avoid using QueryInterface.  When you are using an XPCOM object from Python, you can just call methods and reference properties without regard for the interface that implements it.

When multiple interfaces share the same method or property name, you can use the name of the interface as a differentiator.  Thus, ob.nsIFoo.close() will call close on ob's nsIFoo interface, while ob.nsIBar.close() will use the nsIBar interface.  ob.close() is not defined.