I’ve developed several Python projects in the distant past, and after recently releasing rsnappush and following it up with release 1.1, I’ve researched and learned several new methodologies that Python developers need to be aware of.

A quick preface, though: I only write code for Linux platforms, so compatibility with Windows platforms doesn’t engage me at all. I therefore go with the simplest route for a Linux-only distribution.

setup.py changes: use setuptools

Back in the day Python developers relied on the distutils package, but now setuptools is recommended.

Manpages

I like manpages, and in general, Linux developers like them too. They are predictably found, and follow consistent format that works well for reference purposes. I’m a fan of using markdown as the base format, and then let conversion tools work from there. I considered using pandoc, but it wasn’t specific enough for creating manpages.

My Preferred Tool: ronn

ronn is a great, simple tool for converting markdown into manpages, and has nice manpage-specific markups. For distributing Python command line tools, I’m a fan of having the github-popularized README.md to be the source for documentation. In order to use ronn automatically and get the manpages installed with right permissions, below is the setup.py boilerplate you can re-use. It’s unfortunate that Python doesn’t have better built-in support for distributing manpages in packages.

# setup.py boilerplate
import os
import setuptools
import subprocess
import setuptools.command.sdist
import setuptools.command.install
from distutils import log

manpage = 'rsnappush.1'

def run(cmd):
    log.info("calling " + ' '.join(cmd))
    subprocess.run(cmd, check=True)

# run ronn with every sdist, converting README.md into manpage
class my_sdist(setuptools.command.sdist.sdist):
    def run(self):
        cmd = ["ronn", "--roff", "README.md"]
        run(cmd)
        
        cmd = ["mv", "README.1", manpage]
        run(cmd)
        
        super().run()

# make the manpage world-readable
class my_install(setuptools.command.install.install):
    def run(self):
        os.umask(0o002)
        run(['chmod', '-R', 'a+rX', 'rsnappush.egg-info'])
        
        stat = os.stat(manpage)
        os.chmod(manpage, stat.st_mode | 0o444)
        super().run()

# enter the right hooks for setuptools
setuptools.setup(...,
                 data_files=[('share/man/man1/', [manpage])],
                 cmdclass = {'sdist': my_sdist,
                             'install': my_install,
                 },
)

More uses for README.md

setup.py, in its call to setuptools.setup(), sets long_description. Make your life easy and just have it read README.md:

# More setup.py boilerplate

with open("README.md", "r") as fh:
    long_description = fh.read()
    
setuptools.setup(...
                 long_description = long_description,
                 ...
                 )

Licenses: Mozilla Public License

I did a little more research on Open Source License, and have decided to switch rsnappush from the Eclipse Public License (EPL) to the Mozilla Public License 2.0 (MPL). It’s basically a middle-ground between the BSD and LGPL license. The MPL is very similar to the EPL, but more popular. They basically are both simple copyleft licenses, with no linking rules. The main difference that I could pick out is that the MPL also has a built-in permission that the user can re-license the code as the LGPL if they choose. You can do this with the EPL but I’d have to include an extra clause. I don’t want that extra step.

I plan on using the MPL for much of the new open source code I write.