Inside the Head of PyDanny

Hi, I'm Daniel Roy Greenfeld, and welcome to my blog. I write about Python, Django, and much more. tricks

Seasons greetings!

Before I begin, I want to make very clear that most of what I'm about to explain are 'tricks'. They aren't "best practices", and in at least one case, is possibly inadvisable.

Speaking of inadvisable practices, at some point I'll write a ' traps' blog post, which are things I believe you should never, ever do in a module.


These are tricks I have to make package management in python a tiny bit easier. Before you attempt to implement them, I recommend you have at least basic experience with creating new packages. Two ways to learn about python packaging are the New Library Sprint (beginner friendly) and the Python Packaging User Guide (more advanced).

'python publish'

This is where it all started. One day I was looking at some of Tom Christie's code and discovered the python publish command inside the module of Django Rest Framework. It goes something like this:

import os
import sys

# I'll discuss version tricks in a future blog post.
version = "42.0.0"

if sys.argv[-1] == 'publish':
    os.system("python sdist upload")
    os.system("python bdist_wheel upload")
    print("You probably want to also tag the version now:")
    print("  git tag -a %s -m 'version %s'" % (version, version))
    print("  git push --tags")

# Below this point is the rest of the setup() function

What's awesome about this is that using this technique I don't have to look up the somewhat cryptic python sdist upload command, or the actually cryptic python bdist_wheel upload. Instead, when it's time to push one of my packages to PyPI, I just type:

$ python publish

Much easier to remember!

'python tag'

The problem with Tom Christie's python publish command is that it forces me to type out the git tag command. Okay, let's be honest, it forces me to copy/paste the output of my screen. Therefore, all on my very own, I 'invented' the python tag command:


if sys.argv[-1] == 'tag':
    os.system("git tag -a %s -m 'version %s'" % (version, version))
    os.system("git push --tags")

Pretty nifty, eh? Now I don't have to remember so many cryptic git commands. And I get to shorten the python publish command:

if sys.argv[-1] == 'publish':
    os.system("python sdist upload")
    os.system("python bdist_wheel upload")

When I need to do a version release, I commit my code then type:

$ python publish
$ python tag

Why don't I combine the commands? Well, you aren't supposed to put things like 'RC1' or '-alpha' in your PyPI version names. By seperating the commands I have finer grained control over my package releases. I'm encouraged to place alpha, beta, and release candidates in git tags, rather than formal PyPI releases.

'python test'

I'm fairly certain some of my readers are going to have a seriously problem with this trick. In fact, depending on the the response of those who manage Python's packaging infrastructure, it might be moved to my forthcoming 'traps' blog post.

Alrighty then...

I like py.test. I've blogged about the use of py.test. I try to use it everywhere. Yet, I'm really not a fan of how we're supposed tie it into python test. The precise moment I get uncomfortable with py.test is when it makes me add special classes into

Fortunately, there is another way:

if sys.argv[-1] == 'test':
    test_requirements = [
        modules = map(__import__, test_requirements)
    except ImportError as e:
        err_msg = e.message.replace("No module named ", "")
        msg = "%s is not installed. Install your test requirments." % err_msg
        raise ImportError(msg)

Which means I get to use py.test and python test with a trivial addition of code:

$ python test

In theory, one could run pip install on the missing requirements, or call them from a requirements file. However, since these are 'tricks', I like to keep things short and sweet. If I get enough positive results for this one I'll update this example to include calling of pip for missing requirements.

note: This doesn't mean I'm not using tox. In fact, I use tox to call my version of python test.

What about subprocess?

There are those who will ask, "Why aren't you using the subprocess library for these shell commands?"

My answer to that question is, "Because if I need a nuclear weapon to kill a rabbit maybe I'm overdoing things." For these simple tricks, the os.system() function is good enough.

Why not just use a Makefile?

While I code primarily on Mac OSX and Linux, most of my open source packages are used Windows. Thanks to AppVeyor, I'm testing more and more of them in that environment. In fact, I'll probably be modifying these "tricks" to work better for Windows users.


Stay tuned for my 'traps' blog post to come out early in 2015.


  • 2014/12/21 - Added a note about using tox.
  • 2014/12/21 - Added a note about Makefile and Windows

Published: 2014-12-19 12:00

Tags: python ppoftw


If you read this far, you might want to follow me on twitter or github and subscribe via email below (I'll email you new articles when I publish them).



Content Copyright © 2012-2018 Daniel Greenfeld. Proudly harnessed by Mountain, powered by Flask, and rendered by Frozen Flask, all of which take great advantage of Python.