Skip to main content

If you don't have an IBM ID and password, register here.

By clicking Submit, you agree to the developerWorks terms of use.

The first time you sign into developerWorks, a profile is created for you. This profile includes the first name, last name, and display name you identified when you registered with developerWorks. Select information in your developerWorks profile is displayed to the public, but you may edit the information at any time. Your first name, last name (unless you choose to hide them), and display name will accompany the content that you post.

All information submitted is secure.

The first time you sign in to developerWorks, a profile is created for you, so you need to choose a display name. Your display name accompanies the content you post on developerworks.

Please choose a display name between 3-31 characters. Your display name must be unique in the developerWorks community and should not be your email address for privacy reasons.

By clicking Submit, you agree to the developerWorks terms of use.

All information submitted is secure.

Getting started with PyXPCOM, Part 2

Accessing objects as a client

Uche Ogbuji (uche@ogbuji.net), Principal Consultant, Fourthought, Inc.
Uche Ogbuji is a consultant and co-founder of Fourthought, Inc., a software vendor and consultancy specializing in XML solutions for enterprise knowledge management. Fourthought has developed 4Suite, an open source platform for XML, RDF, and knowledge-management applications. Uche is a computer engineer and writer born in Nigeria; he lives and works in Boulder, Colorado. You can contact Uche at uche@ogbuji.net.

Summary:  Cross-platform component object model (XPCOM) is the component system developed in the Mozilla project. ActiveState has developed an open-source Python library for XPCOM. This three-part series provides a developer's introduction to XPCOM programming in Python. This second part covers the use of PyXPCOM as a client to XPCOM objects.

Date:  01 Jun 2001
Level:  Introductory

Comments:  

Now that you have PyXPCOM installed, you'll find Mozilla scripting and general XPCOM development easier than ever. At this point I suggest you read through a good Mozilla scripting tutorial, especially Neil Deakin's excellent tutorial on Mozilla's XML-based User-interface Language (XUL). Among many other things, Deakin's tutorial covers how to connect to components of the Mozilla project itself. His tutorial is out of date in places, so I would still spend some time in the official documentation of the particular components you're using.

Interfaces with a touch of class

Neil Deakin's XUL tutorial deals with Javascript rather than Python as scripting language, but the PyXPCOM developers purposefully made the Python binding as similar as possible to the Javascript binding. This similarity is a great boon to Python users familiar with Mozilla Javascript programming, and to those who have been through Mozilla Javascript tutorials. First of all, we'll look at the Python form of some of the Javascript examples in Neil Deakin's tutorial.

We'll start off in the Python interactive console. By simply entering:

>>> from xpcom import components

we obtain the components singleton object, which is equivalent to the Components object in Javascript (note the different capitalization). This object represents the XPCOM component registry, allowing access to all the factories and interfaces. The classes attribute of the components object is a dictionary of all the classes registered with XPCOM. The key is the contract ID, and the value is the class object, which also acts as a factory. Therefore, we can invoke the createInstance() method on the class object to create a new instance of the class.

Working with classes and interfaces

Mozilla comes with a class representing local file objects. As an example, a script could use this interface to save downloaded data to files for the user. In the following example we retrieve the class object according to the appropriate contract ID, then create a new instance from the class object.

>>> file_cls = components.classes["@mozilla.org/file/local;1"]
>>> print file_cls
<xpcom.components._Class instance at 0x823c504>
>>> file_abs = file_cls.createInstance()
>>> print file_abs
<XPCOM interface 'nsISupports'>

Notice that the resulting instance is identified as "interface nsISupports". Actually, what we have is a Python proxy object that carries this interface. Remember that this is simply a generic interface that all XPCOM objects provide. To actually call file methods on the object we've created, we must get the appropriate interface. We do this by calling queryInterface(), which returns an object with an interface that allows us to call methods for a local file object. To do this we have to pass in the interface object for local files. The components object also has an interfaces attribute which has one attribute for each registered interface; the attribute name is the interface name used in the IDL. The interface for a local file is nsILocalFile, and its interface ID is the universally unique ID (UUID) "aa610f20-a889-11d3-8c81-000064657374".

>>> file_if = components.interfaces.nsILocalFile
>>> print file_if
{aa610f20-a889-11d3-8c81-000064657374}
>>> file = file_abs.queryInterface(file_if)
>>> print file
<XPCOM interface 'nsILocalFile'>

Invoking object functions

Now that we have a local file object with the proper interface, we can put it to work. A local file object isn't tied to any particular system file at first. You make this association by invoking the initWithPath() method. So, for instance, if we want to delete the file "/tmp/spam.msg", we would create a local file instance, as above, and then initialize it with the file name of interest.

>>> file.initWithPath("/tmp/spam.msg")
 

If you're experimenting, note that the argument to initWithPath() must be a complete file path. If you pass a relative file path such as "spam.msg", you'll get an error. Now we can invoke the delete method to do our deed.

>>> file.delete(0)

The delete method from the nsILocalFile IDL is as follows:

    /**
     *  This will try to delete this file.  The 'recursive' flag
     *  must be PR_TRUE to delete directories which are not empty.
     *
     *  This will not resolve any symlinks.
     */
    void delete(in boolean recursive);

The recursive argument makes sense when you realize that the nsILocalFile interface can represent directories as well as files. As you can see, we passed in the integer 0 for this boolean value. PyXPCOM does a great job of representing the XPCOM data model in a natural way for Python. Python does not have a boolean type, but it treats certain values as false (such as None; empty strings, sequences and mappings; and numbers with zero value). It treats all other values as true. PyXPCOM uses these familiar rules to coerce Python arguments and return values to appropriate boolean values for XPCOM. See the PyXPCOM documentation for the full set of data model rules.

The Python advantage

One of the lovely things about doing any development in Python is the flexibility and cleanliness of its support for dynamic programming. Python is famous for making rapid development in Java actually possible, and PyXPCOM brings the same benefit to XPCOM. One particular aspect of this is the way it makes experimentation at the interactive console so simple.

We have a local file object, but how do we know what to do with it? We can read the IDL and all the documents, but we can also take a quick peek directly at the object. PyXPCOM objects have an _interface_methods_ attribute which is a dictionary of all the available methods. So, for instance, we can do the following:

>>> print file._interface_methods_.keys()
['delete', 'isFile', 'initWithPath', 'isReadable', 'reveal', 
'QueryInterface', 'copyTo', 'copyToFollowingLinksUnicode', 'contains', 
'isWritable', 'launch', 'isSpecial', 'isHidden', 'normalize', 
'append', 'isExecutable', 'create', 'moveToUnicode', 'createUnique', 
'appendRelativeUnicodePath', 'initWithUnicodePath', 'isDirectory', 
'appendRelativePath', 'equals', 'copyToFollowingLinks', 'clone', 
'copyToUnicode', 'isSymlink', 'exists', 'appendUnicode', 'moveTo', 
'spawn']

And presto! We at least know the name of all the available methods. By getting the value from this dictionary, you get a function object with attributes that you can access for further introspective goodies.

I must warn, however, that these attributes with leading underscore are, as in the Python tradition, private, and unsafe to use for anything more than occasional, interactive prodding. They may change name without notice, and they may change behaviour without notice. Of course you can always use the magic function dir() and the magic attribute __dict__ to find out what private variables are available for probing.


Dial PyXPCOM for service

XPCOM services are accessible through PyXPCOM as special singleton objects. To access a service you first get the class object using the contract ID. But you can't call createInstance() on the classes because there is only one true instance against which all requests are made. Instead, you use the getService() method to retrieve a proxy to the service. You pass this method the appropriate interface according to your needs.

As an example, let's access the Mozilla service that provides an interface to user preferences:

>>> from xpcom import components
>>> ps_cls = components.classes["@mozilla.org/preferences-service;1"]
>>> ps = ps_cls.getService(components.interfaces.nsIPrefService)
 

Note that Neil Deakin's tutorial wrongly omits the interface argument to getService() in his example. Now we can piggyback on all the great code the Mozilla developers wrote for user preference management. First of all we read the current user preferences from the configuration file:

>>> ps.readConfigFile()

The configuration settings are arranged in a hierarchy, and we must access its branch to read or update a particular entry. The branches are identified by identifiers separated by dots.

>>> branch = ps.getBranch("browser.startup.")
>>> print branch.getChildList("")
['browser.startup.homepage', 'browser.startup.license_accepted', 
'browser.startup.page', 'browser.startup.autoload_homepage', 
'browser.startup.homepage_override.1']

As we can see from the "browser.startup." root, which deals with the behavior of a browser window when first launched, we get information such as the initial home page and whether or not the user has accepted the Mozilla license. We used the getChildList() method which lists all the names of settings within the branch. Let's have a look at one of them:

>>> print branch.getCharPref("homepage")
chrome://navigator-region/locale/region.properties

You may get a different URL if you have changed Mozilla's initial home page. The getCharPref() method is used to get the value of settings with string value; it takes the name (without root) of the setting as its only argument. Note that you can determine the type of a setting by using the getPrefType() method, which returns an integer representing the type of the given setting. You can add or update a setting in similar fashion:

>>> branch.setCharPref("homepage", "http://ibm.com/developer")

File component + preference component = preference file

As an example of how XPCOM components work together, listing 1 is a Python program that reads in the Mozilla configuration, modifies the browser startup screen, and writes the result of the update to a temporary file ("/tmp/foo.prefs").


Listing 1: Update a user preference settings and write the update to a temporary file
from xpcom import components

#Get the Mozilla preference service
ps_cls = components.classes["@mozilla.org/preferences-service;1"]
ps = ps_cls.getService(components.interfaces.nsIPrefService)

#Use the service to read the default configuration file
ps.readConfigFile()

#Set the setting "browser.startup.homepage" to 
"http://ibm.com/developer"
branch = ps.getBranch("browser.startup.")
branch.setCharPref("homepage", "http://ibm.com/developer")

#Get a local file instance
file_cls = components.classes["@mozilla.org/file/local;1"]
file_abs = file_cls.createInstance()
file = file_abs.queryInterface(components.interfaces.nsILocalFile)

#Set up the new file path
file.initWithPath("/tmp/foo.prefs")

#Create the file using UNIX permissions mode of "775" ("-rw-rw-r--")
file.create(file.NORMAL_FILE_TYPE, 511)

#Save the updated preferences to the new file
ps.savePrefFile(file)


Next: roll your own components

Of course, as with any rich technology, there is always more to discover with PyXPCOM, but this discussion gives you a sound footing on which to continue your further exploration with the PyXPCOM and Mozilla docs and other available resources. In the next and final article in this series we'll discuss how to implement XPCOM objects in Python.


Resources

About the author

Uche Ogbuji

Uche Ogbuji is a consultant and co-founder of Fourthought, Inc., a software vendor and consultancy specializing in XML solutions for enterprise knowledge management. Fourthought has developed 4Suite, an open source platform for XML, RDF, and knowledge-management applications. Uche is a computer engineer and writer born in Nigeria; he lives and works in Boulder, Colorado. You can contact Uche at uche@ogbuji.net.

Report abuse help

Report abuse

Thank you. This entry has been flagged for moderator attention.


Report abuse help

Report abuse

Report abuse submission failed. Please try again later.


developerWorks: Sign in

If you don't have an IBM ID and password, register here.


Forgot your IBM ID?


Forgot your password?
Change your password


By clicking Submit, you agree to the developerWorks terms of use.

 


The first time you sign into developerWorks, a profile is created for you. This profile includes the first name, last name, and display name you identified when you registered with developerWorks. Select information in your developerWorks profile is displayed to the public, but you may edit the information at any time. Your first name, last name (unless you choose to hide them), and display name will accompany the content that you post.

Choose your display name

The first time you sign in to developerWorks, a profile is created for you, so you need to choose a display name. Your display name accompanies the content you post on developerWorks.

Please choose a display name between 3-31 characters. Your display name must be unique in the developerWorks community and should not be your email address for privacy reasons.

(Must be between 3 – 31 characters.)


By clicking Submit, you agree to the developerWorks terms of use.

 


Rate this article

Comments

Help: Update or add to My dW interests

What's this?

This little timesaver lets you update your My developerWorks profile with just one click! The general subject of this content (AIX and UNIX, Information Management, Lotus, Rational, Tivoli, WebSphere, Java, Linux, Open source, SOA and Web services, Web development, or XML) will be added to the interests section of your profile, if it's not there already. You only need to be logged in to My developerWorks.

And what's the point of adding your interests to your profile? That's how you find other users with the same interests as yours, and see what they're reading and contributing to the community. Your interests also help us recommend relevant developerWorks content to you.

View your My developerWorks profile

Return from help

Help: Remove from My dW interests

What's this?

Removing this interest does not alter your profile, but rather removes this piece of content from a list of all content for which you've indicated interest. In a future enhancement to My developerWorks, you'll be able to see a record of that content.

View your My developerWorks profile

Return from help

static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=1
Zone=SOA and web services
ArticleID=86943
ArticleTitle=Getting started with PyXPCOM, Part 2
publish-date=06012001
author1-email=uche@ogbuji.net
author1-email-cc=

Tags

Help
Use the search field to find all types of content in My developerWorks with that tag.

Use the slider bar to see more or fewer tags.

For articles in technology zones (such as Java technology, Linux, Open source, XML), Popular tags shows the top tags for all technology zones. For articles in product zones (such as Info Mgmt, Rational, WebSphere), Popular tags shows the top tags for just that product zone.

For articles in technology zones (such as Java technology, Linux, Open source, XML), My tags shows your tags for all technology zones. For articles in product zones (such as Info Mgmt, Rational, WebSphere), My tags shows your tags for just that product zone.

Use the search field to find all types of content in My developerWorks with that tag. Popular tags shows the top tags for this particular content zone (for example, Java technology, Linux, WebSphere). My tags shows your tags for this particular content zone (for example, Java technology, Linux, WebSphere).