Standard library differences from Python 3
The renamed and reorganised modules you will meet in a 2.7 codebase, and the import line each one becomes in Python 3.
Modules that moved
| Python 2 | Python 3 | Note |
|---|---|---|
urllib2 | urllib.request | Split into request, parse, error, robotparser |
urlparse | urllib.parse | Merged into the urllib package |
ConfigParser | configparser | Renamed, and interpolation semantics tightened |
cPickle | pickle | The fast C implementation is now the only one |
cStringIO / StringIO | io.StringIO / io.BytesIO | Text and bytes streams are separate types |
Queue | queue | Renamed |
HTMLParser | html.parser | Moved into the html package |
httplib | http.client | Renamed |
SocketServer | socketserver | Renamed |
thread | _thread | The low-level module; prefer threading |
# a portable import block, as used by libraries that support both
try:
import configparser
except ImportError: # Python 2
import ConfigParser as configparser
try:
from urllib.request import urlopen, Request
except ImportError:
from urllib2 import urlopen, Request
try:
from urllib.parse import urlencode
except ImportError:
from urllib import urlencodeLibraries such as six and python-future exist precisely to hide this block. Writing it by hand is fine for a small script and becomes noise in a large codebase.
Same name, different behaviour
# 1. StringIO: bytes oriented in 2, and no unicode support from cStringIO
from StringIO import StringIO
buf = StringIO()
buf.write("hello")
print buf.getvalue()
# 2. ConfigParser: percent signs are interpolation markers
import ConfigParser
cfg = ConfigParser.ConfigParser()
cfg.read("app.ini")
value = cfg.get("main", "template") # a literal % must be written %%
# 3. subprocess.check_output exists, but check_ouput text handling differs
import subprocess
out = subprocess.check_output(["git", "rev-parse", "HEAD"]) # returns bytes- In Python 3 configparser defaults to no interpolation via
ConfigParser(interpolation=None), which is often what you want for values that legitimately contain a percent sign. - Python 3 separates text and binary streams strictly:
io.StringIOfor text,io.BytesIOfor bytes. Python 2'sStringIOwas happily both. - Many Python 3 functions gained a
text=Trueoption to return decoded strings; in Python 2 you decode the bytes yourself.
Bridging with six
import six
# type checks that work under both versions
if isinstance(value, six.string_types): # str in 3, (str, unicode) in 2
pass
if isinstance(value, six.text_type): # unicode in 2
pass
if isinstance(value, six.binary_type):
pass
# module aliases without a try/except
from six.moves import configparser, queue, urllib_request
# metaprogramming helpers
@six.add_metaclass(RegistryMeta)
class Plugin(object):
pass⚠️
six makes a codebase run under both interpreters, but it does not make it idiomatic Python 3. Treat it as scaffolding for the migration, not as the destination, and delete the dependency once the last Python 2 target is gone.FAQ
Can I just use the Python 3 import names in a 2to3 run?
Yes, that is exactly what 2to3 does, and it is dependable for these renames. Review the output anyway: the tool rewrites imports but cannot know that your
ConfigParser values contain percent signs.Is cPickle faster than pickle in Python 3?
In Python 3
cPickle no longer exists because pickle already uses the C implementation automatically. Removing the import is the whole change.Related
Migrating to Python 3 Lists, dicts and the Python 2-only APIs
Last refreshed 2026-09-18.