This gist documents the steps that were taken to drop Python 2 support in Pinax Ratings. It is hoped that this will serve as a primer for dropping Python 2 support in other apps in the Pinax project.
Initially, when the app's test suite was run with tox
the system was found to exit with code 1
(signalling abnormal termination) for tests run in the test environment named py27-django111
. This was due to syntax errors being raised when Python 2 incompatible test dependencies are run on Python 2.
The syntax errors were raised because of the following incompatibilities:
-
Type Annotations: The
Pytest 4.6
series was the last Pytest series to support Python 2 (and Python 3.4, for that matter).Pinax-Ratings' test suite makes use of Pytest 5.3.3, which in turn makes extensive use of type annotations.
A trace of a syntax error instance raised because of this is shown below:
File "c:\users\mfonism\appdata\local\temp\easy_install-jqvdua\django-test-plus-1.4.0\.eggs\pytest-5.3.3-py2.7.egg\_pytest\config\__init__.py", line 61 def main(args=None, plugins=None) -> "Union[int, _pytest.main.ExitCode]": ^ SyntaxError: invalid syntax
-
Keyword-Only Arguments: PEP 3102 allows for using a single, bare asterisk
*
as a functional parameter whose sole purpose is to declare that the preceeding arguments can only be supplied by keyword and will never be automatically filled in by a positional argument.The use of this syntax in Pytest 5.3.3 leads to instances of syntax errors while running tests in the offending environment for Pinax-Ratings. Sample trace below:
File "c:\users\mfonism\appdata\local\temp\easy_install-jqvdua\django-test-plus-1.4.0\.eggs\pytest-5.3.3-py2.7.egg\_pytest\assertion\util.py", line 46 def _pformat_dispatch(object, indent=1, width=80, depth=None, *, compact=False): ^ SyntaxError: invalid syntax
-
Yield From: PEP 380 introduced a syntax for delegating to a subgenerator, which is not supported in Python 2.
more-itertools
, a testing(?) dependency no longer supports Python 2, and their use of this syntax is raises syntax errors such as the one outlined below, in the offending environment:File "c:\users\mfonism\appdata\local\temp\easy_install-jqvdua\django-test-plus-1.4.0\temp\easy_install-b8wecm\more-itertools-8.1.0\more_itertools\more.py", line 480 yield from iterable ^ SyntaxError: invalid syntax
-
Function Argument Declaration: The only thing Python 2 allows after
*args
in a function definition is**kwargs
. Python 3 on the other hand opens up more possibilities. The use of one of these possibilities in Pytest 5.3.3 (declaring mandatory keyword args after declaring optional positional args) leads to syntax errors like the one with the trace below:File "c:\users\mfonism\appdata\local\temp\easy_install-jqvdua\django-test-plus-1.4.0\.eggs\pytest-5.3.3-py2.7.egg\_pytest\mark\structures.py", line 54 def param(cls, *values, marks=(), id=None): ^ SyntaxError: invalid syntax
Removing Python 2 from the test matrix (in tox.ini) ensured that no offending test environment was created. Consequently, tests no longer errored out.
The only compatibility utility that was used in this app was python_2_unicode_compatible
. Removing all mentions of this utility had no unfavourable effects on the status of the tests.
try
blocks in import
statements usually signal some form of compatibility handling.
Remove the except block and run the tests. If it fails in any environment, then that compatibility handling is absolutely necessary. Otherwise, it is redundant.
Remove all mentions of the dropped versions of Python from setup.py
, README.md
and related files.
In Pinax Ratings, it was removed from the Supported Django and Python Versions diagram in setup.py
and README.md
, and from the classifiers section of setup.py
.
Prefer str.format()
over %-formatting, as the former is faster and more versatile than the latter.
TO-DO: Drop Python 2 from Circle CI config