Skip to content

Instantly share code, notes, and snippets.

@mfonism
Last active January 21, 2020 02:08
Show Gist options
  • Save mfonism/3b68ab55bb637b603c350bf8027b140c to your computer and use it in GitHub Desktop.
Save mfonism/3b68ab55bb637b603c350bf8027b140c to your computer and use it in GitHub Desktop.
This gist documents the steps that were taken to drop Python 2 support in Pinax Ratings

Dropping Python 2 Support in Pinax-Ratings

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.

Step 1: Remove Python 2 from Test Matrix

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.

Step 2: Check Compatibility Libraries/Utilities

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.

Step 3: Check Compatibility-Handling Try Blocks

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.

Step 4: Update Setup File and Docs

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.

Extra

Prefer str.format() over %-formatting, as the former is faster and more versatile than the latter.

@mfonism
Copy link
Author

mfonism commented Jan 21, 2020

TO-DO: Drop Python 2 from Circle CI config

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment