Introduction to distutils (II)
Hey there! In the first part of this tutorial I showed how to write a simple setup script for distutils (you may want to check that out if you haven't), and now it's time to talk about building and installing the module. Keep on reading and discover the superfoo.
Source code
For the sake of simplicity, I have prepared a really simple python module named superfoo that you can find in my GitHub repository. You can get the source by going to the repository and downloading a ZIP file or if you know your git commands, you can simply clone the source:
$ git clone git@github.com:RMed/superfoo.git
Now that you have the source, you can surely see that its structure looks like this:
superfoo/ -- Directory containing the source
__init__.py -- Module that identifies the directory as a Python package
module_a.py -- Simple module
module_b.py -- Simple module
setup.py -- Setup script
The first build
We are now going to build the source for the first time. First open the setup.py file and modify the author
, author_email
and url
arguments as you like. Once you are done, navigate to the directory with a terminal/command line and execute:
$ python setup.py build
The build command creates a new directory named build and builds everything needed to install. In this case, the module is very simple and does not have any additional scripts or libraries.
Installing the package
Now that we know how to build the package, installing it is as simple as using:
# python setup.py install
Note the # I have written up there. This means that you most likely need to run this command with superuser/administrator permissions.
Once the process is completed, you will have the superfoo package available for use. To try this, enter a python command line and use some of the functions of the package:
>>> import superfoo
>>> superfoo.hello_world()
Hello World!
>>> bar = superfoo.Bar()
>>> super_bar = superfoo.SuperBar("awesome person")
>>> superfoo.hello_name(super_bar.name)
Hello awesome person!
That's as easy as it gets. You can find the package in the dist-packages or site-packages directories of your Python installation, depending on your system.
Custom commands
Another interesting feature that is available in distutils is creating your own commands. The list of available commands in the script is shown by running:
$ python setup.py --help-commands
Imagine that, for any reason, you need a command that provides some functionality not available in your script (compiling locale, copying files, etc). You could create a new class before setup and then make the script recognize it as a command. For instance, let's add a command that prints I'm done! whenever it is called:
from distutils.core import Command
class IsItDone(Command):
# Description of the command that is shown when --help-commands is called
description = "Are you done yet?"
# Here you can specify special options
user_options = []
def initialize_options(self):
""" Include any options prior to execution of the command here """
pass
def finalize_options(self):
""" Include any options after execution of the command here """
pass
def run(self):
""" The function that is executed when the command is used """
print "I'm done!"
Again, this is a really basic example, but should serve as a way to understand the structure of commands.
The next thing to do is to make the script aware of our new command, for which we add a new attribute to the setup()
function:
# Custom commands
cmdclass = {
'doneyet': IsItDone
},
If we now run:
$ python setup.py --help-commands
We should see a new part at the bottom:
Extra commands:
doneyet Are you done yet?
Alright! now let's try running our new command:
$ python setup.py doneyet
running doneyet
I'm done!
Awesome, the command is working as expected, but let's make it even more awesome in the next section.
Overriding commands
Overriding is another useful feature that we can use, for instance, to call the doneyet command after the build has finished. To do this, we create a class like this:
from distutils.command.build import build as _build
class AwesomeBuild(_build):
# Add custom commands
sub_commands = _build.sub_commands + [('doneyet', None)]
def run(self):
_build.run(self)
And then add the command to the list in setup()
, which should now look like this:
setup(
# Module name
name = 'superfoo',
# Module version
version = '1.0.0',
# Short description
description = 'Simple Python module example',
# Author
author = 'YOUR_NAME_HERE',
# Contact email
author_email = 'YOUR_EMAIL_HERE',
# Support url
url = 'YOUR_URL_HERE',
# License (if any)
license = '',
# Packages to include in the build
packages = ['superfoo'],
# Custom commands
cmdclass = {
'build': AwesomeBuild,
'doneyet': IsItDone
},
)
Note that we gave the command the name build so that the original build command is overriden.
That's it for now, next time we will talk about packaging and distributing the modules to the world.
Cheers!