cheat sheet
six
Package-level reference for six on PyPI — what it did, why it still appears in dependency trees, and why new code should avoid it.
six
What it is
six is a single-file utility library that papered over the differences between Python 2 and Python 3 from roughly 2010 through Python 2's end-of-life in 2020. It exposed renamed builtins (six.moves.urllib, six.string_types, six.iteritems), portable metaclass syntax (six.with_metaclass), and small compatibility shims (six.PY2, six.PY3, six.text_type). The library's name comes from "2 × 3" — bridging the two Python lines.
In 2026 six is mostly legacy. Python 2 is six years past EOL; new code should target Python 3.8+ directly and use no six constructs at all. The package still appears in many dependency trees because long-lived libraries (notably python-dateutil, several Django plugins, OpenStack components, AWS-internal packages) haven't migrated. Reach for six only when modifying code that already depends on it.
Install
pip install six
Output: (none — exits 0 on success; pure-Python, zero dependencies)
uv add six
Output: dependency resolved + added to pyproject.toml
poetry add six
Output: updated lockfile + virtualenv install
You almost never install six directly — it arrives transitively. If you find yourself adding it to a new project, stop and reconsider.
Versioning & Python support
- Current version is
1.16.0(released 2021). The library is in maintenance mode; no further feature releases are planned. - Supports Python 2.7 and Python 3.4+. Effectively all modern Python 3 versions work.
1.17.xmay ship to address security or packaging issues, but the API is frozen.
Package metadata
- Maintainer: Benjamin Peterson (CPython core dev)
- Project home: github.com/benjaminp/six
- Docs: six.readthedocs.io
- PyPI: pypi.org/project/six
- License: MIT
- Governance: single-maintainer; effectively frozen
- First released: 2010
- Downloads: still in PyPI top 20 by transitive use, despite Python 2 being long gone
Optional dependencies & extras
- None.
sixis intentionally a single-file pure-Python module.
Alternatives
| Package | Trade-off |
|---|---|
| Native Python 3 idioms | The right answer in 2026. dict.items() instead of six.iteritems(d); urllib.parse instead of six.moves.urllib.parse; metaclass=... directly instead of six.with_metaclass. |
future | Larger compatibility shim — explicit Python-2-from-Python-3 backports. Even more legacy than six. |
python-modernize | A CLI to mechanically rewrite Py2 code into Py2/3 compatible code using six. Useful only when migrating away from Py2. |
2to3 (stdlib, removed in 3.13) | Mechanical converter; produces Py3-only output. Use when you no longer need Py2 support. |
Common gotchas
six.movesis lazy —from six.moves import urllibworks, butfrom six.moves.urllib import parsemay not on some setups (depends on import-hook timing).six.string_typesis(str,)on Py3,(str, unicode)on Py2. New Py3 code should just checkisinstance(x, str).six.text_typeisstron Py3,unicodeon Py2. In Py3-only code, you can drop it entirely.six.b("...")was the way to writebytesliterals on both Py2 and Py3. Py3 supportsb"..."natively — use that.six.iteritems(d)isd.iteritems()on Py2,iter(d.items())on Py3. In Py3-only code, just writed.items().six.with_metaclass(M, B)was the portable metaclass spell. Py3 syntaxclass C(B, metaclass=M)is native — use it.- Dependency conflicts. A newer library says
sixis unused (it removed the dep), butpython-dateutilstill pulls it in. Don't try to removesixfromrequirements.txtdirectly — it appears transitively.
Real-world recipes
Modern Python 3 — drop six entirely
These are the patterns to prefer in new code; six is shown only for comparison.
Recipe 1 — Dict iteration.
# six (legacy)
from six import iteritems
for k, v in iteritems(my_dict):
print(k, v)
# Modern Python 3
for k, v in my_dict.items():
print(k, v)
Output: identical iteration order and behavior. In Py3, dict.items() returns a view, not a list — usually what you want.
Recipe 2 — String types.
# six (legacy)
import six
if isinstance(x, six.string_types):
...
# Modern Python 3
if isinstance(x, str):
...
Output: Py3 has one string type. Use it directly.
Recipe 3 — Metaclass syntax.
# six (legacy)
import six
class MyClass(six.with_metaclass(MyMeta, BaseClass)):
...
# Modern Python 3
class MyClass(BaseClass, metaclass=MyMeta):
...
Output: native syntax is cleaner and the type checker understands it correctly.
Recipe 4 — Renamed module imports.
# six (legacy)
from six.moves.urllib.parse import urlparse
from six.moves import range, queue, cPickle as pickle
# Modern Python 3
from urllib.parse import urlparse
import queue
import pickle
Output: stdlib paths are stable; six.moves only confuses IDEs and type checkers.
Patterns still seen in 2026
The patterns you might still encounter when reading old code:
Recipe 5 — six.PY2/six.PY3 branching.
import six
if six.PY2:
# legacy Py2-only branch (effectively dead code in 2026)
raise RuntimeError("Python 2 not supported")
Output: since Py2 is EOL, the six.PY2 branch is dead code. Modernization checklist: delete those branches outright.
Recipe 6 — six.b() for byte literals.
import six
data = six.b("hello") # b'hello' on both Py2 and Py3
# Modern: just write b"hello"
data = b"hello"
Output: identical bytes; the modern form removes the dependency.
Recipe 7 — Bridging code that hasn't migrated yet.
# If you must touch legacy Py2/3 code that still uses six, keep using six
# until the next refactor lets you remove it. Don't mix idioms within one module.
import six
for k, v in six.iteritems(d):
...
Output: consistent style within a single legacy module reduces review friction; full removal is a separate task.
Performance tuning
sixis fast. Each shim is a single attribute lookup. No measurable perf overhead in any normal use.- Removing
sixfrom your code is the only "tuning" — and the gain is import-time and clarity, not runtime. six.moveslazy imports can cause first-use latency in cold-start environments (Lambda). Lazy-import dateutil etc. instead.
Version migration guide
The only meaningful migration is away from six, not between six versions. A typical sequence:
- Identify all
siximports:grep -r "import six\|from six" src/ - Replace each per the patterns above (or use
pyupgrade --py3-plus/ruff --select UP). - Remove
sixfromrequirements.txt/pyproject.toml(if it was a direct dep). - Run tests on the lowest Python version you still support.
- If
sixstill shows inpip freeze, it's transitive — wait for the upstream library to drop it.
# Before
import six
print(six.PY3, six.iteritems({"a": 1}), six.with_metaclass)
# After (Py3-only)
print(True, {"a": 1}.items(), "use metaclass= keyword")
Output: functionally equivalent; one fewer dependency.
Tooling that helps:
pyupgrade --py3-plus— mechanically removes mostsixusage.ruff --select UP—UPrules flagsix.iteritems,six.string_types, and friends.python-modernize(older) — produces Py2/3-compat code; useless if you're Py3-only.
Security considerations
sixitself has no known CVEs. It's pure Python with no I/O, no parsing of untrusted input.- Pin a recent version (
six>=1.16) — older versions had minor packaging issues but no security ones. - The real security concern is unmaintained code that depends on
six— if a library hasn't droppedsixby 2026, ask whether it's getting other security updates.
Testing & CI
There's almost nothing to test in six itself. The useful CI check is "do we have any six usages we missed":
# Fail CI if any new code imports six
git diff main HEAD -- 'src/**/*.py' | grep -E '^\+.*\b(import six|from six)' && exit 1 || true
Output: exits 1 if a PR added a new six import; useful as a pre-merge gate.
# Inventory transitive six usage
pip show six | grep "Required-by"
Output: lists which installed packages still pull in six (e.g. python-dateutil, some Django plugins).
Ecosystem integrations
six is a dependency of, not an integrator with — the question is "which packages still pull it in":
python-dateutil— uses it on PyPy and edge platforms; remains a transitive source.- Older Django plugins — many were last updated pre-EOL and still depend on
six. - OpenStack components — historically heavy
sixusers; many still do. urllib3 1.26.x— DROPPEDsixin2.x. The 1.26 line still pulls it.tensorflow,pandas,boto3— all droppedsixby 2022-2023.
Compatibility matrix
| Python | six | Notes |
|---|---|---|
| 2.7 | 1.16 | Final useful target. |
| 3.4 | 1.16 | Stable. |
| 3.5-3.7 | 1.16 | Stable. |
| 3.8 | 1.16 | Stable. |
| 3.9 | 1.16 | Stable. |
| 3.10 | 1.16 | Stable. |
| 3.11 | 1.16 | Stable. |
| 3.12 | 1.16 | Some collections imports warn; pinned 1.16.0 includes the fix. |
| 3.13 | 1.16 | Continues to work; effectively no benefit. |
Production deployment
- Don't actively install
six. Treat it as a smell whenever it appears as a direct dependency. - Tolerate it as a transitive.
pip show sixlistingpython-dateutilas a dependent is fine; just don't write newsixcode. - Modernization audits. Once a year, run
ruff --select UP(orpyupgrade --py3-plus) and remove any remainingsixusage you control. - Track upstream drops. When
python-dateutilfinally dropssix(announced but not yet released as of 2026), your installs lose the transitive dep automatically.
When NOT to use this
- New code, new project, anything modern. Don't.
- You support only Python 3.8+. Don't.
- You're tempted to "future-proof" against a Py2 comeback. That's not happening.
Reach for six only when:
- Maintaining existing legacy code where consistent style matters.
- A library you must use still imports
sixsymbols and you're patching it locally.
Troubleshooting common errors
| Error / Symptom | Likely cause | Fix |
|---|---|---|
ModuleNotFoundError: No module named 'six.moves.urllib' | Lazy import not registered | Restart the interpreter; verify six is the imported module, not a stale .pyc. |
AttributeError: module 'six' has no attribute 'PY3' | Importing a six.py from your own project | Rename your local file. |
DeprecationWarning from six on Python 3.12+ | Old six version | Upgrade to six>=1.16.0. |
Tools flag six.iteritems calls | Linter rules (UP) firing | Replace with .items() — the linter is right. |
pip install -U six "doesn't help" | six is already at max version | The library is in maintenance mode. |
See also
- Python: installation — modern Python 3 setup
- Official six docs
- Python.org porting guide
- pyupgrade — automated modernization