Removing Python 3.6 Support from My Packages
Python 3.6 reached its end of life on the 23rd December. As its release manager put on the Python forum, it has gracefully “ridden off into the sunset”.
Yesterday I followed up my book release with some relaxing maintenance to remove Python 3.6 support from the packages I maintain. I used the myrepos
tool to make PRs in parallel with a script, as I’ve previously covered. This meant I could update and release 34 packages within a couple of hours.
In this post I’ll cover some of what that entailed, in case it helps you update your own projects.
2022-01-11-drop-python-3.6.sh
Below is the script I used, followed by some explanation. You can also find this script within my scripts repo, alongside other “modify repo” scripts.
#!/bin/sh
set -eu
git diff --exit-code
git checkout main
git pull
sd ' +- 3\.6\n' '' .github/workflows/main.yml
sd ' +py36.*\n' '' tox.ini
sd -s 'py{36,37' 'py{37' tox.ini
sd -s "target-version = ['py36']" "target-version = ['py37']" pyproject.toml
sd '\[--py36-plus' '[--py37-plus' .pre-commit-config.yaml
sd -s "Python 3.6 to " "Python 3.7 to " README.rst
sd ' +Programming Language :: Python :: 3\.6\n' '' setup.cfg
sd -s 'python_requires = >=3.6' 'python_requires = >=3.7' setup.cfg
sd -f m ' +subprocess\.run\((\n|.)*?python3\.6(\n|.)*?\)\n' '' requirements/compile.py
rm requirements/py36*txt
# shellcheck disable=SC2016
sd -f m '(=======
History
=======)' '$1
* Drop Python 3.6 support.' HISTORY.rst
git add .github/workflows/main.yml tox.ini pyproject.toml .pre-commit-config.yaml README.rst setup.cfg requirements/ HISTORY.rst
if [ -f docs/installation.rst ]; then
sd -s "Python 3.6 to " "Python 3.7 to " README.rst
git add docs/installation.rst
fi
git switch -c drop_python_3.6
git commit -m "Drop Python 3.6 support
Its EOL was 2021-12-23: https://www.python.org/dev/peps/pep-0494/#lifespan ."
git push -u origin "$(git rev-parse --abbrev-ref HEAD)"
gh pr create --fill
gh pr merge --squash --delete-branch --auto
A brief explanation:
set -eu
enables some strictness options. I always use these as they reduce surprises from shell scripts.The three first
git
commands check that the repository has no uncommitted changes, is on its itsmain branch
, and up to date.sd
is a find-and-replace tool. It’s a Rust-based alternative to the classicsed
. I find it easier to use.These
sd
commands rewrite Python 3.6 references in known places throughout my repository files:.github/workflows/main.yml
- configuration for GitHub Actions, the CI system.tox.ini
- configuration for tox, the test runner.pyproject.toml
- configuration for Black, the formatter..pre-commit-config.yaml
- configuration for pyupgrade, the syntax updater.README.rst
- installation instructions.setup.cfg
- classifiers and minimum Python version configuration for PyPI.requirements/compile.py
- a script for running pip-compile for each tox environment.HISTORY.rst
- the changelog.
There’s a conditional block around rewriting
docs/installation.rst
, that checks for its existence. Most of my projects use only their README for documentation, so they don’t have adocs/
directory, and attempting tosd
the file fails. But Django-MySQL uses Sphinx, withindocs/
, so I want to rewrite that.The remaining
git
commands create a branch, commit it, and push it to GitHub.The
gh
commands are the GitHub CLI, being used to create a PR and set it to auto-merge if the tests pass.
pypugrade Rewrites
Shout outs to pyupgrade, which removes old blocks guarded by outdated sys.version_info
checks. This feature saved me a bunch of fiddly changes. pre-commit.ci ran pyupgrade for me, and pushed the changes back to the PR.
For example, in django-linear-migrations, pyupgrade rewrote this block:
from types import ModuleType
if sys.version_info >= (3, 7):
def is_namespace_module(module: ModuleType) -> bool:
return module.__file__ is None
else:
def is_namespace_module(module: ModuleType) -> bool:
return getattr(module, "__file__", None) is None
…to just the Python 3.7+ side:
from types import ModuleType
def is_namespace_module(module: ModuleType) -> bool:
return module.__file__ is None
Cool.
Remaining References
A handful of repositories had extra references to Python 3.6/3.7 that needed removing. I found these with this rg
(ripgrep) command:
rg --color always -g '!*.txt' '\b3\b.*\b[67]\b' | less
--color always
keeps the colour when piped to less
, which helped me skim through the results.
New Versions
After merging the respective PRs, I released new versions of all packages. For those still on Python 3.6, Pip will obey the python_requires
field and find the older versions. If you use any packages please upgrade and check everything’s okay for you.
Newly updated: my book Boost Your Django DX now covers Django 5.0 and Python 3.12.
One summary email a week, no spam, I pinky promise.
Related posts:
Tags: python