six
raw JSON → 1.17.0 verified Tue May 12 auth: no python install: verified quickstart: verified maintenance
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.
pip install six Common errors
error ModuleNotFoundError: No module named 'six' ↓
cause The `six` library is not installed in the Python environment being used.
fix
pip install six
error ImportError: cannot import name 'cStringIO' from 'six.moves' ↓
cause `six.moves` provides a Python 2/3 compatible `StringIO` class, but does not expose `cStringIO` directly, which was a Python 2-specific module.
fix
from six.moves import StringIO
error TypeError: a bytes-like object is required, not 'str' ↓
cause Code intended for Python 2 (where `str` was bytes) is run in Python 3 (where `str` is Unicode), and a function expects actual bytes.
fix
Use
six.ensure_binary(your_string) or six.b(your_string) to explicitly convert the string to a bytes object for Python 2/3 compatibility. error AttributeError: 'dict' object has no attribute 'iteritems' ↓
cause In Python 3, dictionary methods like `iteritems()` were removed, replaced by `items()`, which return view objects.
fix
Use
six.iteritems(my_dict) for Python 2/3 compatible iteration, or my_dict.items() if the codebase is Python 3 only. 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.). ↓
fix Remove six and use Python 3 native equivalents. Tools like 'pyupgrade' and 'ruff --select UP' can automate the removal of six patterns.
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. ↓
fix Always use the current PyPI release (1.17.0). If a dependency vendors its own stale six, override it in sys.modules before importing that library: import six; import sys; sys.modules['lib.vendor.six'] = six
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. ↓
fix Rename your file or variable. Check with: python -c "import six; print(six.__file__)" to verify the correct module is being loaded.
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. ↓
fix Use 'from six.moves.urllib.parse import urlencode' etc., rather than 'from six.moves import urllib' followed by 'urllib.parse.urlencode()'.
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. ↓
fix Use six >= 1.8.0. Prefer with_metaclass() over add_metaclass() when __slots__ are involved, or switch to Python 3 native metaclass= syntax.
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. ↓
fix Use the underscore-separated Python 3 name: 'from six.moves import html_parser'. Refer to https://six.readthedocs.io/#module-six.moves for the full mapping table.
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. ↓
fix Use 'import sys; sys.version_info >= (3, 4)' for version gating instead of six.PY34.
Install compatibility verified last tested: 2026-05-12
python os / libc status wheel install import disk
3.10 alpine (musl) - - 0.01s 17.8M
3.10 slim (glibc) - - 0.00s 18M
3.11 alpine (musl) - - 0.02s 19.7M
3.11 slim (glibc) - - 0.01s 20M
3.12 alpine (musl) - - 0.01s 11.6M
3.12 slim (glibc) - - 0.01s 12M
3.13 alpine (musl) - - 0.01s 11.2M
3.13 slim (glibc) - - 0.01s 12M
3.9 alpine (musl) - - 0.01s 17.3M
3.9 slim (glibc) - - 0.01s 18M
Imports
- six
import six - string_types / text_type / binary_type wrong
isinstance(value, str)correctimport six isinstance(value, six.string_types) - six.moves (stdlib remaps) wrong
import six.movescorrectfrom six.moves import urllib from six.moves import range from six.moves import configparser - with_metaclass wrong
class MyClass(object, metaclass=Meta): pass # Py3-only syntaxcorrectfrom 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 wrong
from six.moves import urllib urllib.parse.urlencode(…)correctfrom six.moves.urllib.parse import urlencode from six.moves.urllib.request import urlopen
Quickstart verified last tested: 2026-04-23
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))