Upgrade from Python 2 to 3
Python 3 is growing in acceptance and usage. People in all sorts of fields are using Python, and many of them are switching to Python 3. This article will list down repeating problem (task) that I encounter during migration of my Pyramid project from Python 2 to Python 3. Previously posted as part 1 and part 2, I merged the two parts in this article. Here the problems, and of course the solution:
class advice impossible in python3. use the @implementer class decorator instead...
More or less similar with this issue:
zope.interface has had a feature for 10 years under Python2 which can't be made to work portably under Python3. It is spelled like:
from zope.interface import Implements
class Foo(object):
implements(IFoo)
There is a newer, Python3-compatible spelling, which the pyramid trunk now uses (as of today):
from zope.interface import implementer
@implementer(IFoo)
class Foo(object):
pass
That spelling only works under Python >= 2.6
ImportError: No module named 'urlparse'
simply refer to this page:
urlparse is part of the standard Python 2 library. It's shipped as part of Python; it isn't packaged separately on PyPI et al. urlparse.urlparse (the function) was renamed in Python 3 to urllib.parse.
If you need to write code which is Python2 and Python3 compatible you can use the following import:
try:
from urllib.parse import urlparse
except ImportError:
from urlparse import urlparse
Or if you work only with Python 3, following import is enough:
from urllib.parse import urlparse
ImportError: No module named 'ConfigParser'
In Python 3, ConfigParser has been renamed to configparser for PEP 8 compliance. So, to minimize changes, what I normally do:
import configparser as ConfigParser
ImportError: No module named 'StringIO'
The StringIO and cStringIO modules are gone. Instead, import the io module and use io.StringIO or io.BytesIO for text and data respectively.
try:
from StringIO import StringIO
except ImportError:
from io import StringIO
ImportError: No module named 'htmlparser'
Module html.parser defines a class HTMLParser which serves as the basis for parsing text files formatted in HTML (HyperText Mark-up Language) and XHTML.
from html.parser import HTMLParser
Sample:
from html.parser import HTMLParser
class MyHTMLParser(HTMLParser):
def handle_starttag(self, tag, attrs):
print("Encountered a start tag:", tag)
def handle_endtag(self, tag):
print("Encountered an end tag :", tag)
def handle_data(self, data):
print("Encountered some data :", data)
parser = MyHTMLParser()
parser.feed('<html><head><title>Test</title></head>'
'<body><h1>Parse me!</h1></body></html>')
ImportError: No module named httplib
In Python 3, the module has been renamed to http.client
import http.client
httpRequest = ""
conn = http.client.HTTPConnection("localhost",8080)
conn.request("GET","/file.html",httpRequest)
response = conn.getresponse()
print(response.status,response.reason)
conn.close();
ImportError: No module named urllib2
In python 3 urllib2 was merged into urllib. To make Python 2 code work in Python 3:
try:
import urllib.request as urllib2
except ImportError:
import urllib2
But if not, here a sample for urllib.request
import urllib.request
wp = urllib.request.urlopen("http://goggle.com")
pw = wp.read()
print(pw)
ImportError: No module named 'BaseHTTPServer'
BaseHTTPServer, SimpleHTTPServer modules in Python 2 have been merged into http.server module in Python 3. Sample:
import http.server
import ssl
httpd = http.server.HTTPServer(('localhost', 4443), http.server.SimpleHTTPRequestHandler)
httpd.socket = ssl.wrap_socket(httpd.socket, certfile='server.pem', server_side=True)
httpd.serve_forever()
TypeError: the JSON object must be str, not 'bytes'
solve it using decode():
result = json.loads(response.readall().decode('utf-8'))
TypeError:Unicode-objects must be encoded before hashing
Refer to this similar issue, It is because looking for a character encoding:
line.encode('utf-8')
or another way around:
str(line)
Upgrade Python 2 to 3
NameError: global name 'unicode' is not defined
Python 3 renamed the unicode type to str, the old str type has been replaced by bytes.
if isinstance(unicode_or_str, str):
text = unicode_or_str
decoded = False
else:
text = unicode_or_str.decode(encoding)
decoded = True
In short; use str(*)
iteritems in Python
Based on this stackoverflow question: Why was iteritems() removed from Python 3? Seems like a terrific and useful method. What's the reasoning behind it?
In Python 2.x - .items() returned a list of (key, value) pairs.
In Python 3.x, .items() is now an itemview object, which behaves different - so it has to be iterated over, or materialised... So, list(dict.items()) is required for what was dict.items() in Python 2.x.
In Python 2.7:
common_keys = list(dict_a.viewkeys() & dict_b.viewkeys())
Will give you a list of the common keys, but in Python 3.x - just use .keys() instead.
common_keys = list(dict_a.keys() & dict_b.keys())
Python 3.x has generally been made to be more "lazy" - i.e. map is now effectively itertools.imap, zip is itertools.izip, etc.
'module' object has no attribute 'urlencode'
It's about urllib. In Python 3.x Do
import urllib.parse
instead. urlencode is part of urllib.parse
ImportError: cannot import name 'quote'
Again about urllib. Similar like problem above you can do
from urllib.parse import quote
For direct import
dictionary changed size during iteration
It's a problem in Python 3, in short; use list...
As explained, in Python 2.x calling keys makes a copy of the key that you can iterate over while modifying the dict:
for i in d.keys():
Note that this doesn't work in Python 3.x because keys returns an iterator instead of a list. Another way is to use list to force a copy of the keys to be made. This one also works in Python 3.x:
for i in list(d):