3

I am writing some code that I want to be able to run on previous versions of Python before type-hinting was added without needing a separate code base. Is there a simple way to accomplish this?

Similar to how from __future__ import print_function would allow you to use print() in Python 2 code, is there a from __future__ import type_hints?

The accepted answer to the duplicate question does provide one way to make it work in 2.7, but it doesn't say if that should also work fine in Python 3. I'm going to ask in the comments of the answer, but my question is looking for something compatible with both Python 2 and earlier versions of Python 3.

Rob Rose
  • 1,806
  • 22
  • 41
  • 1
    Possible duplicate of [Type hinting in Python 2](https://stackoverflow.com/questions/35230635/type-hinting-in-python-2) – Max Malysh May 08 '18 at 15:08
  • 2
    @MaxMalysh That's close, but I'm not just looking for Python 2, also interested in earlier versions of Python 3. – Rob Rose May 08 '18 at 15:09

2 Answers2

9

To use type hints in a fully backwards-compatible way, you will need to...

  1. Always use the comment-based syntax: Python 2 does not support function annotations; Python 3.0-3.5 do not support variable annotations.
  2. Install the backport of the typing module when using Python 2.7 and 3.0 - 3.4. The typing module was added to the standard library in Python 3.5 and must be pip-installed for earlier versions of Python.

One additional complication is that the typing module was updated several times with new types since it was added to the standard library in Python 3.5.0 -- types like ClassVar, Deque, Protocol, Text, Type, to name just a few.

If you want to use those types and still support Python 3.5 and 3.6, additionally install the typing_extensions module. You can find a full list of backported types on the github repo.

Basically, if you want to use any of the types listed in the github repo linked above, and support Python 3.5.0 - 3.6.x, always import them from typing_extensions instead of from typing.


Some additional details and caveats that you may or may not care about:

  1. About typing_extensions:

    If you plan on using typing_extensions, also pay careful attention if you need to support Python 3.5.0 - 3.5.2. The typing module went through several, often substantial, internal changes since it was first released in Python 3.5.0.

    The typing_extension module tries to bridge between these different internal APIs in a sane way, but there's always the chance that something was overlooked. The latest minor versions of Python 3.5 and Python 3.6 are much more up-to-date though, and so are far less likely to have issues.

    (You might also be able to get away without using typing_extensions if you want to support only the latest minor versions of Python 3.5 and 3.6: several of the types that were missing in Python 3.5.0 and Python 3.6.0 were added later on. But it's honestly hard to keep track of what was added when, so it might be safer to just default to using typing_extensions and not worry about it.)

  2. About mypy:

    If want to use mypy, keep in mind that mypy can be run using only non EOL'd versions of Python 3. So, at time of writing, Python 3.4+.

    However, mypy itself can be used to analyze Python 2.7+ code.

  3. About typeshed and Python 3.0 - 3.2:

    Mypy, and most other PEP 484-compliant type checking tools, rely on typeshed, a collection of type annotations for the standard library and popular 3rd party libraries.

    Typeshed keeps track of when functions and classes were added to the standard library. That way, you can ask tools like mypy to make sure your code works with specific versions of Python and that you didn't accidentally import anything from the future.

    However, typeshed only keeps track of this info for Python 2.7 and 3.3+. So, you need to be careful if you're targeting specifically Python 3.0 - 3.2.

  4. About unicode_literals and mypy/typeshed:

    Some people recommend using unicode_literals as a technique to help with Python 2/3 compatibility.

    However, I believe using unicode_literals causes a number of issues with either typeshed or mypy. I forget the exact details, but the upshot is that you're probably better off not using it (at least for the time being).

    Instead, avoid unicode-related issues by using the type system to your advantage. Specifically:

    • Use typing.Text when something MUST be unicode. This type is aliased to unicode in Python 2 and str in Python 3.
    • Use bytes (or maybe bytearray?) when something MUST be bytes. Be sure to keep in mind that the bytes behaves slightly differently between Python 2 and 3.
    • Use str when that a value should be whatever str means for that particular version of Python.

    If you need to write a function that needs to work with multiple kinds of strings, you can do so with careful use of Union or AnyStr.

Michael0x2a
  • 58,192
  • 30
  • 175
  • 224
0

The accepted answer to the duplicated question seems to be valid for Python 3.x where x < 5, at least for mypy.

From mypy docs:

The example below illustrates the Python 2 function type annotation syntax. This syntax is also valid in Python 3 mode:

from typing import List

def hello(): # type: () -> None
    print 'hello'

class Example:
    def method(self, lst, opt=0, *args, **kwargs):
        # type: (List[str], int, *str, **bool) -> int
        """Docstring comes after type comment."""

However prior to Python 3.5 you need to pip install typing.

abukaj
  • 2,582
  • 1
  • 22
  • 45