setuptools is a collection of enhancements to the Python distutils that allow you to more easily build and distribute Python packages, especially ones that have dependencies on other packages.
Now just to be clear, setuptools has a lot of features... however, so far I've really only needed two of them. Setting up console scripts, and enabling plugins to tie into an application. I expect that as I use setuptools more and more, there will be much more that I will want to add here.
You might be reading this and wonder, "Why not just follow the code examples from PEAK?". Well.. that is simple, as with many of my articles, I always try to keep the beginner in mind. And far too often, sifting through the full documentation with 100 different ways to do something gets well confusing. I'm hoping that breaking it down a bit further with some complete examples might help someone move towards adopting setuptools into their daily tool kit. That said, this article is an overview of a the basic usage of setuptools.
The following code sets up a basic python command line application that will be used to add setuptools to. Create the files to follow along... or don't. The first file we look at is the __init__.py within the helloworld module. We will deal with this later when we talk about plugins, however for now just create an empty file.
Note: Before starting, you should be working out of a current directory called 'helloworld'. Therefore, from within that directly, there is another 'helloworld' directory also... the second level directory is the helloworld library that gets installed.
$ pwd /Users/you/helloworld
./helloworld/__init__.py:
# leave file empty
./helloworld/core.py:
#!/usr/bin/env python class HelloWorld(object): def __init__(self): pass def say_hi(self, name=None): if name: print 'Hello World! My name is %s.' % name else: print 'Hello World!' def main(): h = HelloWorld() h.say_hi('johnny') if __name__ == '__main__': main()
Note: core.py is an arbitrary name for the file, that is just a personal preference as a starting point (though is common practice by many).
So, with this simple application I quickly say hi to everyone by running it directly (via __main__):
$ python helloworld/core.py Hello World! My name is johnny.
Now, that's all and good... but most likely if this is a command line utility/application, it will be expected to be accessible via a common location like '/usr/bin/helloworld'. Now, technically I could simply install helloworld/core.py to /usr/bin/helloworld but that isn't what we are going for here, is it? (is it???)
| Attachment | Size |
|---|---|
| helloworld-1.tar.gz | 384 bytes |
This article is a continuation of Getting Started with setuptools.
entry_points are a dictionary mapping entry point group names to strings or lists of strings defining the entry points. Entry points are used to support dynamic discovery of services or plugins provided by a project. See Dynamic Discovery of Services and Plugins for details and examples of the format of this argument. In addition, this keyword is used to support Automatic Script Creation.
The following is an example setup.py that automatically creates our '/usr/bin/helloworld' (or similar) console script:
./setup.py:
#!/usr/bin/env python from setuptools import setup, find_packages import sys, os version = '0.1' setup(name='helloworld', version=version, description="My Hello World Application", long_description="""My even longer description about Hello World.""", classifiers=[], keywords='', author='Your Name', author_email='you@example.com', url='', license='', packages=find_packages(exclude=['ez_setup', 'examples', 'tests']), include_package_data=True, zip_safe=False, install_requires=[ # -*- Extra requirements: -*- ], entry_points=""" [console_scripts] helloworld = helloworld.core:main """ )
After creating the setup.py, I can now install it or because we are still working on it, we can use the setuptools develop mode feature:
$ sudo python setup.py develop running develop running egg_info writing helloworld.egg-info/PKG-INFO writing top-level names to helloworld.egg-info/top_level.txt writing dependency_links to helloworld.egg-info/dependency_links.txt writing entry points to helloworld.egg-info/entry_points.txt reading manifest file 'helloworld.egg-info/SOURCES.txt' writing manifest file 'helloworld.egg-info/SOURCES.txt' running build_ext Creating /opt/local/Library/Frameworks/Python.framework/Versions/2.6/lib/python2.6/site-packages/helloworld.egg-link (link to .) helloworld 0.1 is already the active version in easy-install.pth Installing helloworld script to /opt/local/Library/Frameworks/Python.framework/Versions/2.6/bin Installed /Users/you/helloworld Processing dependencies for helloworld==0.1 Finished processing dependencies for helloworld==0.1
Note: Using 'develop' rather than 'install' allows you to deploy your project in "development mode", such that it's available on sys.path, yet can still be edited directly from its source checkout.
We should now be able to call the command line utility 'helloworld' right away:
$ helloworld Hello World! My name is johnny.
Depending on the system you are working on (OSX in this example) depends on where the console script gets created to. On most Linux distributions this should end up being /usr/bin or /usr/local/bin. In my case it is in a deep, convoluted path that Apple chose. If we take a look at the script it creates you'll see how it is calling our application:
#!/usr/bin/env python # EASY-INSTALL-ENTRY-SCRIPT: 'helloworld==0.1','console_scripts','helloworld' __requires__ = 'helloworld==0.1' import sys from pkg_resources import load_entry_point sys.exit( load_entry_point('helloworld==0.1', 'console_scripts', 'helloworld')() )
And so... because we registered our helloworld.core.main() function as an entry point for the helloworld console script, when you run it the entry point is identified via pkg_resources.load_entry_point() and therefore main() is called.
Solid?
| Attachment | Size |
|---|---|
| helloworld-2.tar.gz | 780 bytes |