Python Development Environments

I have spent a substantial number of years mostly programming in Perl, but this year I have been working on a team that has a strong preference for Python and so I have converted across.

Previously I have dabbled in Python - I could read basic python, and had patched various things - mainly Ansible - in the past, but had not done any serious work in the language.

However its not the transfer to the language thats caused most of the headaches, but other things surrounding that - for example:-

  • Development environment
  • Formatting, checking, rules for how things go into git
  • Documentation

We don’t have much in the way of stated rules for these, and despite Python being much more about there being one right way to do things, in terms of overall development environments it all appears that there is a huge variety of ways that people do stuff…

So this is what I am now moving to for things I am working on…

Development Environment

I use Poetry. This brings together management of virtual environments, dependency lists etc as well as distribution builds and pushing of packages to pypi or to a local repository.

The downside is that you tend to have to run things by poetry run cmd..., but I mostly set that up in a Makefile to deal with that. Additionally at present the cost of running builds and deploys in CI is bloated by the additional time to install poetry - I may build myself an appropriate Docker image to ameliorate that.

The Makefile I add is hand crafted - mostly cargo-culted from that last thing I did - but I now use the self documenting Makefile trick to add a set of help to the Makefile.

Version number handling is managed by bump2version - using that to rewrite the poetry version tag as well as updating the information within the package __init__.py file (yes this may well be superfluous but the alternatives appear to require loading additional modules just to find your own version number). Additionally I found a hack this last week to make bump2version update the CHANGELOG.md file by using the alternate methods of Markdown formatting (otherwise the # header markers cause the bump2version config file to ignore the lines as a comment).

So in ‘.bumpversion.cfg’ you have:-

[bumpversion:file:CHANGELOG.md]
search = <!-- insertion marker -->
replace = <!-- insertion marker -->
        [{new_version}] - {now:%Y-%m-%d}
        --------------------

and in CHANGELOG.md you have:-

Unreleased Changes
------------------

<!-- insertion marker -->
- last commit changelog entry

Pre-Commit Formatting / Checking

I have a .pre-commit-config.yaml file in each repository - mostly exactly the same, but with some per-project tweaks. I run pre-commit prior to each commit - mostly not currently enforced by actually adding it to git hooks, although I may change that as I did previously have a set of hooks set up by git-hooks to run the perl TidyAll

A sample .pre-commit-config.yaml file can be found here, but in general I am running a set of checks including the black formatter, flake8 and mypy.

In terms of git management I currently tend to work in Fork having previously used Tower for many years (Tower is likely a bit more powerful and slick, but it now costs more per year than Fork does outright - although I hope that Fork will not regret going for a one off cost model).

Documentation

Documentation is another thing that seems odd regarding python - there is the start of a standard workflow defined with docstrings etc, and then things go a bit freestyle. I have to say I am not very keen on reStructuredText, and Sphinx felt opaque to me - and then you hit several different API documentation mechanisms, many of which appear to be machine rather than human readable. This is one of the areas where the Perl simple but consistant documentation setup works better - although the python side does pull more introspection information out of the code.

So I have settled on one I like (so far - only been working in this for a few weeks) and that appears to work well with my other tools.

I use MkDocs. With that I have been using the Material for MkDocs theme. Stacked on top of that are the mkdocs-click extension for CLI tools, and mkdocstrings for API documentation.

There is one little problem with the pipeline above in that mkdocs-click and mkdocstrings use different function signature markup - this is something I mean to have a look at in the future.

CI etc

The CI workflow varies not least because I release some things on Github using Travis CI, and other things are handled by an internal GitLab instance using GitLab CI. However in both cases I have got a fairly standard test, release and build documentation workflow with the artifacts going to the appropriate repo and documentation going to Github/GitLab Pages.

I am not currently doing a lot of additional multi-version testing with tox etc. I may move to this later, but am trying to take this a step at a time.