Took me way too long to get back to my own topic thanks to the new RC - sorry for that
You can actually skip a ton of these steps - or at least it worked fine on my end to do that...
virtualenv has an argument
--python with which you can specify the Python interpreter with which to create the venv. So it boils down to installing both 2.7 and 3.7 somewhere and then telling
virtualenv which interpreter to use:
virtualenv --python=C:/Python27/python.exe venv27 virtualenv --python=C:/Python37/python.exe venv37
activate whichever one you need, or - that's how I do it - configure both interpreters in your IDE and then switch there. The only bad thing about the latter is that at least my (slightly outdated) version of PyCharm always needs an extra minute to get back on track after that, and I have to make sure to close all active run/debug panels relating to the old Python environment or it doesn't properly switch
Does that support 2 and 3 concurrently? I always took it to be more a case of "once that's run the code won't run under Py2 anymore", which at least for now would be counter productive.
What I've made good use of is this cheat sheet by the Python-Future project:
And in general it seems to be a good idea to turn on Py3 behaviour as far as possible in your source files through various
from __future__ imports. E.g. (from OctoPrint's code base):
# -*- coding: utf-8 -*- from __future__ import absolute_import, division, print_function, unicode_literals
I also have a post-it note attached to my main screen for more than a year now that currently says
Instead of just
except: for broad catch-all blocks. Not Python 3 specific, but something we switched to in the code base with the Py3 changes.
Two helper methods built into OctoPrint (
octoprint.util package) that should hopefully make it easier to ensure the expected data type. Will keep their hands off of anything that's already in the correct type.
open if I remember correctly, though I cannot for the life of me right now remember what exactly was special about that. Pretty sure it had something to do with encoding though (because that's the worst about this whole migration).
This is what @OutsourcedGuru already mentioned, a ton of the functional programming kinda methods in Python 3 are now returning generators instead of ready data, so if you want to do stuff with that later where it needs to be a list you'll need to convert yourself. Has kicked me in the teeth a couple of times now, one of the fixes that went into RC6 was actually another one of these.
Meta classes work differently in Py2 (
__metaclass__ field) and Py3 (
metaclass= argument in the class declaration). In an incompatible way of course. So yeah, this works best:
from future.utils import with_metaclass class Foo(with_metaclass(FooType, BarType)): pass
And then there's this result of searching for
except ImportError: in OctoPrint's code base:
try: # noinspection PyCompatibility import queue # Py3 except ImportError: # noinspection PyCompatibility import Queue as queue # Py2
try: # noinspection PyCompatibility from collections.abc import ChainMap # Py3, built-in except ImportError: # noinspection PyCompatibility from chainmap import Chainmap # Py2, external dependency
try: # noinspection PyCompatibility from collections.abc import KeysView # Py3 except ImportError: # noinspection PyCompatibility from collections import KeysView # Py2
try: # noinspection PyCompatibility from os import scandir, walk # Py3, built-in except ImportError: # noinspection PyCompatibility from scandir import scandir, walk # Py2, external dependency
try: # noinspection PyCompatibility from html.parser import HTMLParser # Py3 except ImportError: # noinspection PyCompatibility from HTMLParser import HTMLParser # Py2
try: # noinspection PyCompatibility from http.client import HTTPResponse # Py3 except ImportError: # noinspection PyCompatibility from httplib import HTTPResponse # Py2
try: # noinspection PyCompatibility from http.server import HTTPServer, BaseHTTPRequestHandler # Py3 except ImportError: # noinspection PyCompatibility from BaseHTTPServer import HTTPServer, BaseHTTPRequestHandler # Py2
try: # noinspection PyCompatibility from urllib.parse import urlencode # Py3 except ImportError: # noinspection PyCompatibility from urllib import urlencode # Py2
urllib.parse.unquote(same goes for
try: # noinspection PyCompatibility from urllib.parse import unquote # Py3 except: # noinspection PyCompatibility from urllib import unquote # Py2
try: # noinspection PyCompatibility from urllib import parse as urlparse # Py3 except ImportError: # noinspection PyCompatibility import urlparse # Py2
collections.Iterableand Python 3.8 (!):
try: from collections import Iterable except ImportError: # Python >= 3.8 from collections.abc import Iterable
Now ain't that fun?