Tips on Developing Python Projects for PyPI
I wrote two recent articles on Python packaging: Distributing Python Packages Part I: Creating a Python Package and Distributing Python Packages Part II: Submitting to PyPI. I was able to get a couple of my programs packaged and submitted.
Ongoing Development and Testing
But then I realized all was not quite right. I could install new releases of my package -- but I couldn't run it from the source directory any more. How could I test changes without needing to rebuild the package for every little change I made?
Fortunately, it turned out to be fairly easy. Set PYTHONPATH to a directory that includes all the modules you normally want to test. For example, inside my bin directory I have a python directory where I can symlink any development modules I might need:
mkdir ~/bin/python ln -s ~/src/metapho/metapho ~/bin/python/
Then add the directory at the beginning of PYTHONPATH:
export PYTHONPATH=$HOME/bin/python
With that, I could test from the development directory again, without needing to rebuild and install a package every time.
Cleaning up files used in building
Building a package leaves some extra files and directories around,
and git status
will whine at you since they're not
version controlled. Of course, you could gitignore them, but it's
better to clean them up after you no longer need them.
To do that, you can add a clean
command to setup.py.
from setuptools import Command class CleanCommand(Command): """Custom clean command to tidy up the project root.""" user_options = [] def initialize_options(self): pass def finalize_options(self): pass def run(self): os.system('rm -vrf ./build ./dist ./*.pyc ./*.tgz ./*.egg-info ./docs/sphinxdoc/_build')(Obviously, that includes file types beyond what you need for just cleaning up after package building. Adjust the list as needed.)
Then in the setup()
function, add these lines:
cmdclass={ 'clean': CleanCommand, }
Now you can type
python setup.py cleanand it will remove all the extra files.
Keeping version strings in sync
It's so easy to update the __version__ string in your module and forget that you also have to do it in setup.py, or vice versa. Much better to make sure they're always in sync.
I found several version of that using system("grep...")
,
but I decided to write my own that doesn't depend on system()
.
(Yes, I should do the same thing with that CleanCommand, I know.)
def get_version(): '''Read the pytopo module versions from pytopo/__init__.py''' with open("pytopo/__init__.py") as fp: for line in fp: line = line.strip() if line.startswith("__version__"): parts = line.split("=") if len(parts) > 1: return parts[1].strip()
Then in setup()
:
version=get_version(),
Much better! Now you only have to update __version__ inside your module and setup.py will automatically use it.
Using your README for a package long description
setup has a long_description for the package, but you probably already have some sort of README in your package. You can use it for your long description this way:
# Utility function to read the README file. # Used for the long_description. def read(fname): return open(os.path.join(os.path.dirname(__file__), fname)).read()
long_description=read('README'),
[ 10:15 Dec 22, 2016 More programming | permalink to this entry | ]