Ride the carousel of folly with a Python migration
mortoray.com
mortoray.com
for line in iter( handle.stdout.readline, '' ):
self.output.put( ( handle, line ) )
> This no longer terminated an emitted lots of odd values (one number repeatedly). It turns out it returns an empty b'' now when empty, as opposed to a ''. This could have been avoided had this API sensibly returned None instead of an empty string.For any observers, this is much more idiomatically written:
for line in handle.stdout:
self.output.put((handle, line))
That is, the default iteration over a file-like object is line by line. Frankly, I wonder how many Python programmers are aware that iter() has a two-arg form; this is the first time I've ever seen it used (and I had to look it up myself).> [PyPI]
His solution of maintaining separate code bases is … unusual. I feel like a cursory glance around the library landscape shows that most of the projects out there are maintaining a single codebase that supports 2.7+. six greatly simplifies most of this. I'm not aware of any projects that are supporting Python 2 and 3 concurrently by separating out their code completely.
> I presumed that I’d just open a “Migration from Python 2 to 3 Guide” and follow the instructions. Alas, there is no such guide. There appears to be no central resource whatsoever on the porting of code from version 2 to version 3.
An I'm Feeling Lucky search on that exact quote pulls up the migration guide in Python's official documentation.[1]
Heck. I haven't done any 2 to 3 porting. I just read a few articles out of idle curiosity once and even I know 2to3 doesn't work like that.
Well, running 2to3.py -w -n -o [new_py3_path] [old_py2_path] got me a copy of a project with every fix already applied. Another alternative I've been thinking is doing a "last before 2to3 commit" (and leaving no pending changes), and then run 2to3 inplace and check those from the IDE (which may or may not be easier to a few folks that work with me).
I have to check a few more things, but it seems 2017 is the year I'm finally porting then two biggest projects I work on to Python 3. Luckily both projects aren't heavy offenders and should still remain Py2 compatible, but we may have the push with the clients to ask for exlusively Py3 support.
If you have uncommitted changes, instead of dropping them in as "last before..." you can git-stash them before branching and git-stash-pop them when you come back.
It's an argument of preference in practice, and therefore all but guaranteed to go nowhere. I tend to think that updating a whole codebase like this is best done atomically rather than piecemeal, especially when the code belongs to a team rather than an individual. But I can see where opinions might differ on such a point.
I backported a py3 project to use py2 (on behalf of a few users) and it was a real pain in the ass to have a wheel that allowed for both versions.
I ended up having a py3 build on github, and using a TravisCI build that ran pasteurize and pushed to PyPI that version. At this point, I don't even know what the travis.yml file is doing (100 lines), and regret a bit backporting the project.
All I had to do was create a setup.cfg that said:
[wheel]
universal = 1
To build, I use: python setup.py sdist bdist_wheel
twine upload -s dist/*
The .travis.yml is just 7 lines. https://github.com/cgmb/guardonce/blob/v2.0.0/.travis.ymlIf you want to do both, then you need pasteurize, and end up with a travis mess: https://github.com/sergiocorreia/panflute/blob/master/.travi...
To be honest, for me the morale of the story is that supporting py2 is just not worth it. I am not going to use it, so why support it at all?
One thing that I notice is that your project is a library, while mine is a program. I wonder if that also plays a role.
I saw that migration guide, but it has very few details about what is actually involved in migration. It doesn't include API change details.
If you saw the migration guide but found it incomplete, you should edit your post to say that rather than saying there isn't one.
Interesting article though, thank you for posting it.
I guess if everything went smoothly, he wouldn't have had a blog post to write.
1) Hard-to-read or quirky code. Unicode handling, args, harder unpacking, etc. The more time it passes, the more updates py3 gets, and the more outdated the py2 code looks.
For instance, after a year almost exclusively in py3, I have completely forgotten about iter(), unicode gimmics, etc. and also rely more on stuff that is just too slow on py2 (like ordered dicts). With 3.6, I can start assuming dicts are ordered, which will create further breaks for py2 users (if they want to backport anything).
2) Outdated libraries. As the other comments say, epydoc is abandonware. Had he migrated to py3 earlier, he would have stopped investing in that tool earlier and switched to the standard (sphynx).
3) Harder to interact with others in the community.
Although the folks at google might think otherwise, most people are now using py3, and most code being developed right now is on py3 (at least open source code).
If you are still on py2, it will be harder to hire (it hints at huge technical debt, and a tedious working environment), harder to sell to others (e.g. I don't use google compute because of that), and harder to collaborate on (for open source projects).
All in all, the more that py2 and py3 diverge, the harder it will be for legacy (py2) users to migrate (this was not the case a few years ago, when there were still important libraries on py2 only).
Citation? Still feels about 50/50 to me and even that number feels a little generous to Py3.
I guess a masochist could do something like pipe 1 to 'patch -p0' or something…
I ran into the same unicode issues as the author, and dumped all the code that handled it because I didn't need it in 3.
I also reduced the whole codebase by some 150 lines because of not needing to do a bunch of extra work I originally designed in when working through the code. This was more due to fresh eyes.
ETA: the code was on 2.7 because a library I needed wasn't yet available under python 3 (FuelSDK from SalesForce) until November, and I completely missed this. 1.0 version supports py 3, but it wasn't in pip.
"there is nothing in the PyPI package description that actually indicates what version of Python is being targeted"
is incorrect. There are Trove classifiers[1] for both versions, and indeed for every minor version number of both versions. There are even Python 2 and 3 classifiers that end in "Only", which look like they would exactly fit the author's use case.
However, since pip 9.0.0, it will respect the Requires-Python[0] metadata, which can be specified via your setup.py file.
Since PyPI allows only a single source distribution per release, though, this will still not get you separate modules for separate codebases as it seems you originally desired. As mentioned in a previous comment, it's generally preferred to support multiple versions in a single codebase via six.
[0]: https://www.python.org/dev/peps/pep-0345/#requires-python
Sphinx is close to a standard nowadays.