mozilla

Revision 127030 of Creating a Python XPCOM component

  • Revision slug: Creating_a_Python_XPCOM_component
  • Revision title: Creating a Python XPCOM component
  • Revision id: 127030
  • Created:
  • Creator: Ssmedberg
  • Is current revision? No
  • Comment /* Preparation */ - major language clean-up

Revision Content

Creating Applications with Mozilla already provides a tutorial for making a simple JavaScript or C++ component (implementing the nsISimple interface), here is how to make the same component in Python using PyXPCOM.

(Note that some details may be missing)

Preparation

The PyXPCOM library is already provided with Mozilla. To use the library, tell Python where it can find the PyXPCOM library. This is probably best done by adding a "mozillalibs.pth" (any name will work) to your Python site packages directory (e.g. "/usr/lib/python2.3/site-packages"), with only one line, the location of the packages you may want to import (the "/dist/bin/python" of your mozilla source).

Then you can

import xpcom

in any Python module (mostly, in your component).

Mozilla will be needed to handle Python, although this may require special compilation. It may possible to build the PyXPCOM stuff seperately, but that is not addressed in this document.

To build Mozilla, get and compile the source, with the following options in the .mozconfig file (in the root of your Mozilla source).

. $topsrcdir/browser/config/mozconfig
mk_add_options MOZ_OBJDIR=@TOPSRCDIR@/fb-opt-static
ac_add_options --enable-optimize
ac_add_options --disable-debug
ac_add_options --enable-static
ac_add_options --disable-shared
ac_add_options --disable-tests
ac_add_options --disable-short-wchar
ac_add_options --enable-extensions=default,python/xpcom

The two last lines are the most important for this process.

Now, launch the build.

Note: Previous versions of the PyXPCOM library are provided here, although download and use of an old version is not recommended.

Defining the interface

Make a file named "py_simple.idl", to define the interface:

#include "nsISupports.idl"
[scriptable, uuid(2b324e9d-a322-44a7-bd6e-0d8c83d94883)]
interface nsIPySimple : nsISupports
{
    attribute string yourName;
    void write( );
    void change(in string aValue);
};

(this is the same as the nsISimple interface used here ; theoretically we could use the same file, as we can have several components sharing an interface)

You should pay special attention to types here - Python and JavaScript are both loosely-typed, so it's fairly easy to expect to be able to pass anything from one to the other. I got bit by that one, sending a unicode string from javascript and expecting to receive a unicode string in Python, whereas only a "string" was defined in the interface file (use a wstring if you want unicode. See this discussion for a bit of info on unicode and javascript).

See here for info on describing interfaces, and on which types can be used.

Registring the interface

In the "components" directory, execute :

../xpidl -m typelib -w -v -I /usr/share/idl/mozilla/ nsIPySimple.idl

xpidl will then create nsIPySimple.xpt (which has to be in the right place, for example in the "components" directory).

Implementing the component

This is way simpler than with C++ ! PyXPCOM does a lot of the work for you.

Make a file named "py_simple.py" for the actual code (again in the 'components': directory)

from xpcom import components, verbose

class PySimple: #PythonTestComponent
    _com_interfaces_ = components.interfaces.nsIPySimple
    _reg_clsid_ = "{c456ceb5-f142-40a8-becc-764911bc8ca5}"
    _reg_contractid_ = "@mozilla.org/PySimple;1"
    def __init__(self):
        self.yourName = "a default name" # or mName ?

    def __del__(self):
        if verbose:
            print "PySimple: __del__ method called - object is destructing"

    def write(self):
        print self.yourName

    def change(self, newName):
        self.yourName = yourName

That's all! Then, you have to register your component; the procedure is the same as for any component, but this won't work if Python components weren't enabled.

To register the component, touch the .autoreg in the bin directory (it's a hidden file). This can also be done by deleting xpti.dat. Then, the next time Mozilla starts, it will rebuild the index of components, including any new one in the 'components' directory (Thus, it's better to start mozilla from the command line to see if new components register successfully).

Testing it

To see this work, you will have to start Firefox from the command line, since that'll be where the stuff will be printed out.

External links

Revision Source

<p>
</p><p><a class="external" href="http://books.mozdev.org/html"><i>Creating Applications with Mozilla</i></a> already provides  <a class="external" href="http://books.mozdev.org/html/mozilla-chp-8-sect-2.html">a tutorial</a> for making a simple JavaScript or C++ component (implementing the <code>nsISimple</code> interface), here is how to make the same component in Python using <a href="en/PyXPCOM">PyXPCOM</a>.
</p><p>(Note that some details may be missing)
</p>
<h4 name="Preparation"> Preparation </h4>
<p>The PyXPCOM library is already provided with Mozilla.  To use the library, tell Python where it can find the PyXPCOM library. This is probably best done by adding a "mozillalibs.pth" (any name will work) to your Python site packages directory (e.g. "/usr/lib/python2.3/site-packages"), with only one line, the location of the packages you may want to import (the "/dist/bin/python" of your mozilla source).
</p><p>Then you can 
</p>
<pre class="eval">import xpcom
</pre>
<p>in any Python module (mostly, in your component).
</p><p>Mozilla will be needed to handle Python, although this may require special compilation. It may possible to <a class="external" href="http://kb.mozillazine.org/Standalone_PyXPCOM">build the PyXPCOM stuff seperately</a>, but that is not addressed in this document.
</p><p>To build Mozilla, <a href="en/Build"> get and compile the source</a>, with the following  options in the .mozconfig file (in the root of your Mozilla source).
</p>
<pre class="eval">. $topsrcdir/browser/config/mozconfig
mk_add_options MOZ_OBJDIR=@TOPSRCDIR@/fb-opt-static
ac_add_options --enable-optimize
ac_add_options --disable-debug
ac_add_options --enable-static
ac_add_options --disable-shared
ac_add_options --disable-tests
ac_add_options --disable-short-wchar
ac_add_options --enable-extensions=default,python/xpcom
</pre>
<p>The two last lines are the most important for this process.
</p><p>Now, launch the build.
</p><p>Note:  Previous versions of the PyXPCOM library are provided <a class="external" href="http://aspn.activestate.com/ASPN/Downloads/Komodo/PyXPCOM/">here</a>, although    download and use of an old version is not recommended.
</p>
<h4 name="Defining_the_interface"> Defining the interface </h4>
<p>Make a file named "py_simple.idl", to define the interface:
</p>
<pre>#include "nsISupports.idl"
[scriptable, uuid(2b324e9d-a322-44a7-bd6e-0d8c83d94883)]
interface nsIPySimple : nsISupports
{
    attribute string yourName;
    void write( );
    void change(in string aValue);
};
</pre>
<p>(this is the same as the nsISimple interface used <a class="external" href="http://books.mozdev.org/html/mozilla-chp-8-sect-2.html">here </a>; theoretically we could use the same file, as we can have several components sharing an interface)
</p><p>You should pay special attention to types here - Python and JavaScript are both loosely-typed, so it's fairly easy to expect to be able to pass anything from one to the other. I got bit by that one, sending a unicode string from javascript and expecting to receive a unicode string in Python, whereas only a "string" was defined in the interface file (use a wstring if you want unicode. See <a class="external" href="http://aspn.activestate.com/ASPN/Mail/Message/pyxpcom/2484414">this discussion</a> for a bit of info on unicode and javascript).
</p><p>See <a class="external" href="http://www.mozilla.org/scriptable/xpidl/idl-authors-guide/rules.html">here</a> for info on describing interfaces, and on which types can be used.
</p>
<h5 name="Registring_the_interface"> Registring the interface </h5>
<p>In the "components" directory, execute :
</p>
<pre>../xpidl -m typelib -w -v -I /usr/share/idl/mozilla/ nsIPySimple.idl
</pre>
<p>xpidl will then create nsIPySimple.xpt (which has to be in the right place, for example in the "components" directory).
</p>
<h4 name="Implementing_the_component"> Implementing the component </h4>
<p>This is <i>way</i> simpler than with C++ ! PyXPCOM does a lot of the work for you.
</p><p>Make a file named "py_simple.py" for the actual code (again in the 'components': directory)
</p>
<pre class="eval">from xpcom import components, verbose

class PySimple: #PythonTestComponent
    _com_interfaces_ = components.interfaces.nsIPySimple
    _reg_clsid_ = "{c456ceb5-f142-40a8-becc-764911bc8ca5}"
    _reg_contractid_ = "@mozilla.org/PySimple;1"
    def __init__(self):
        self.yourName = "a default name" # or mName ?

    def __del__(self):
        if verbose:
            print "PySimple: __del__ method called - object is destructing"

    def write(self):
        print self.yourName

    def change(self, newName):
        self.yourName = yourName
</pre>
<p>That's all! Then, you have to register your component; the procedure is the same as for any component, but this won't work if Python components weren't enabled.
</p><p>To register the component, touch the .autoreg in the bin directory (it's a hidden file). This can also be done by deleting xpti.dat. Then, the next time Mozilla starts, it will rebuild the index of components, including any new one in the 'components' directory (Thus, it's better to start mozilla from the command line to see if new components register successfully).
</p>
<h4 name="Testing_it"> Testing it </h4>
<p>To see this work, you will have to start Firefox from the command line, since that'll be where the stuff will be printed out.
</p>
<h3 name="External_links"> External links </h3>
<ul><li> <a class="external" href="http://books.mozdev.org/html/mozilla-chp-8-sect-2.html">Creating XPCOM components</a>, on which this short tutorial is based.
</li><li> A three-parts tutorial on <a class="external" href="http://www-128.ibm.com/developerworks">ibm developWorks</a>:
<ul><li> <a class="external" href="http://www-128.ibm.com/developerworks/webservices/library/co-pyxp1/">Getting to know PyXPCOM</a> - info on building PyXPCOM (and maybe Mozilla) to get it to work.
</li><li> <a class="external" href="http://www-128.ibm.com/developerworks/webservices/library/co-pyxp2.html">Getting started with PyXPCOM, part 2</a> - accessing xpcom from Python.
</li><li> <a class="external" href="http://www-128.ibm.com/developerworks/webservices/library/co-pyxp3/">Getting started with PyXPCOM, part 3</a> - Creating your own compomnents. The problem with this one is that the <a class="external" href="http://www-128.ibm.com/developerworks/webservices/library/co-pyxp3/listing2.html">sample code they give</a> is slightly broken (it ends with a "retu" which shouldn't be there. I haven't tried it, but it looks like nothing is missing, so getting rid of the "retu" should make it work fine.).
</li></ul>
</li></ul>
Revert to this revision