six
six is a Python 2 and 3 compatibility library that provides utility functions for smoothing over differences between the two Python versions, enabling codebases to run on both without modification. It consists of a single Python file (six.py) covering string types, integer types, standard library moves, metaclass helpers, and more. Current version is 1.17.0, released December 2024. The project is in maintenance mode: Python 2 reached end-of-life in 2020, and in 2024 it is entirely normal for projects to support only Python 3, so new code should not take a dependency on six — it is primarily relevant for maintaining legacy dual-version codebases or as a transitive dependency.
Warnings
- deprecated six exists solely to support Python 2/3 dual-compatibility. Python 2 reached end-of-life in January 2020. New projects targeting Python 3 only should not use six — use native Python 3 syntax instead (e.g. 'class Foo(metaclass=Meta)', 'from urllib.parse import urlencode', f-strings, etc.).
- breaking six.moves is a virtual module backed by a meta path hook in six.py — there is no moves.py file on disk. In environments with frozen importers, custom loaders, or when a vendored/stale copy of six is present (e.g. inside a third-party library's vendor folder), 'import six.moves' or 'from six.moves import X' can raise ModuleNotFoundError even though 'import six' succeeds.
- gotcha Naming any of your own files 'six.py' or any variable 'six' shadows the installed library and causes confusing ImportError or AttributeError failures downstream.
- gotcha six.moves.urllib sub-modules (parse, request, error, response, robotparser) must each be explicitly imported before use. Accessing them as attributes of a previously imported 'urllib' alias from six.moves is not reliably populated until the sub-module is imported.
- gotcha six.add_metaclass() with __slots__ containing '__weakref__' or '__dict__' was silently broken before 1.8.0 — the decorator re-created the class without preserving slot semantics correctly.
- gotcha six.moves aliases follow Python 3 naming conventions with dots replaced by underscores (e.g. html.parser → html_parser, http.client → http_client). Importing the Python 2 module name directly (e.g. 'from six.moves import HTMLParser') will raise ImportError.
- gotcha six.PY34 (a constant that was True when running on Python 3.4+) was present in some versions and relied upon by downstream libraries but is not part of the documented public API. It was removed in later releases, breaking code that imported it directly.
Install
-
pip install six
Imports
- six
import six
- string_types / text_type / binary_type
import six isinstance(value, six.string_types)
- six.moves (stdlib remaps)
from six.moves import urllib from six.moves import range from six.moves import configparser
- with_metaclass
from six import with_metaclass class MyClass(with_metaclass(Meta, Base)): pass
- add_metaclass
from six import add_metaclass @add_metaclass(Meta) class MyClass(object): pass
- reraise
from six import reraise reraise(tp, value, tb=None)
- six.moves.urllib
from six.moves.urllib.parse import urlencode from six.moves.urllib.request import urlopen
Quickstart
import six
from six.moves import urllib
from six.moves.urllib.parse import urlencode
# Version guard
if six.PY2:
print("Running on Python 2")
else:
print("Running on Python 3")
# Cross-version type check
value = u"hello"
assert isinstance(value, six.text_type) # unicode on Py2, str on Py3
assert isinstance(b"bytes", six.binary_type) # str on Py2, bytes on Py3
assert isinstance(value, six.string_types) # catches both str/unicode on Py2
# Stdlib move: urllib.parse.urlencode works on both versions
params = urlencode({"key": "value", "foo": "bar"})
print("Encoded:", params)
# Integer types (int + long on Py2, int only on Py3)
assert isinstance(42, six.integer_types)
# Metaclass compatibility
from six import with_metaclass
class Meta(type):
pass
class MyBase(with_metaclass(Meta, object)):
pass
print("Metaclass:", type(MyBase))