For pure Python packages, the sdist
(source distribution) command has always been enough. For C/C++ packages however, it is usually not that convenient. The problem with C/C++ packages is that compilation is needed unless you use a binary package. Traditionally those were generally the .egg
files but they never really solved the issue quite right. That is why the wheel
format has been introduced (PEP 0427), a binary package format that contains both source and binaries and can install on both Windows and OS X without requiring a compiler. As an added bonus, it installs faster for pure Python packages as well.
Implementation is luckily simple. First, install the wheel
package:
# pip install wheel
Now you'll be able to use the bdist_wheel
command to build your packages. The only small gotcha is that by default the packages created by Python 3 will only work on Python 3, so Python 2 installations will fall back to the sdist
file. To fix that, you can add the following to your setup.cfg
file:
[bdist_wheel] universal = 1
The only important thing to note here is that in the case of C extensions, this can go wrong. The binary C extensions for Python 3 are not compatible with those from Python 2. So if you have a pure Python package and are targeting both Python 2 and 3, enable the flag. Otherwise just leave it as the default.
Once you have everything up and running, tested, and documented, it is time to actually push the project to the Python Package Index (PyPI). Before pushing the package to PyPI, we need to make sure everything is in order.
First, let's check the setup.py
file for issues:
# python setup.py check running check warning: check: missing required meta-data: url warning: check: missing meta-data: either (author and author_email) or (maintainer and maintainer_email) must be supplied
It seems that we forgot to specify a url
and the author
or maintainer
information. Let's fill those:
import setuptools eggs = setuptools.Extension('eggs', sources=['eggs.pyx']) setuptools.setup( name='Eggs', version='1.0', ext_modules=[eggs], setup_requires=['Cython'], url='https://wol.ph/', author='Rick van Hattem (Wolph)', author_email='[email protected]', )
Now let's check again:
# python setup.py check running check
Perfect! No errors and everything looks good.
Now that our setup.py
is in order, let's try testing. Since our little test project has virtually no tests, this will come up close to empty. But if you're starting a new project, then I recommend trying to maintain 100 percent test coverage from the beginning. Implementing all the tests later is usually more difficult, and testing while you work generally makes you think more about the design decisions of the code. Running the test is easy enough:
# python setup.py test running test running egg_info writing dependency_links to Eggs.egg-info/dependency_links.txt writing Eggs.egg-info/PKG-INFO writing top-level names to Eggs.egg-info/top_level.txt reading manifest file 'Eggs.egg-info/SOURCES.txt' writing manifest file 'Eggs.egg-info/SOURCES.txt' running build_ext skipping 'eggs.c' Cython extension (up-to-date) copying build/... -> --------------------------------------------------------------------- Ran 0 tests in 0.000s OK
Now that we have all in check, the next step is building the documentation. As mentioned earlier, the sphinx
and sphinx-pypi-upload-2
packages can help here:
# python setup.py build_sphinx running build_sphinx Running Sphinx v1.3.5 ...
Once we are certain that everything is correct, we can build the package and upload it to PyPI. For pure Python releases, you can use the sdist
(source distribution) command. For a package that uses a native installer, there are a few options, such as bdist_wininst
and bdist_rpm
, available. I personally use the following for nearly all my packages:
# python setup.py build_sphinx upload_sphinx sdist bdist_wheel upload
This automatically builds the Sphinx documentation, uploads the documentation to PyPI, builds the package with the source, and uploads the package with the source.
This will obviously only succeed if you are the owner of that specific package and are authorized with PyPI.