Breaking code like this was a big mistake in my opinion - it resulted in many people sticking on the old version for code and library compatibility. There are still libraries which don't work on Python 3, though now fairly few of them.
In addition to that, the unicode changes were in my opinion another mistake - they made it far more complicated to deal with strings and byte arrays as now even for the simplest of applications you have to put thought into character encodings. Another annoyance for me personally is that I often use python to do things like hex and base64 encoding, the removal of .encode("hex") really wrecked this one for me. Now I have to remember weird import libraries like "binascii" with weird function names like "hexlify".
Ultimately Python 2 works. And it works well, there just aren't that many compelling reasons to move to 3 given that it often is more complicated and breaks existing code. They have a few cool things now, so I sometimes use it for new code but there's still nothing that's making me want to delve into a port of my older codebases.
In term's of code reliability, Python 3's approach is much more sane.
Ruby released the source-incompatible Ruby 1.9 just months before Python 3. IIRC most versions of Swift have been source-incompatible with each other. Even a language as buttoned-down as C++ has made source-incompatible changes[1]. I have no idea where people got the notion that Python invented the idea of breaking changes, but it just isn't so.
[1]: https://stackoverflow.com/questions/6399615/what-breaking-ch...
It's a difference of a few minor things versus a whole swath of changes to the handling of strings in the language, heck, a python 2 print statement isn't compatible with python 3.
These are much more significant breaking changes which make porting code much harder than any of the other examples you've given. For the most part, developers don't have to touch or only have to make minor changes for specific cases to port to Ruby 1.9 or C++11 - look at those C++11 changes for example, so few people were doing those things and they're such bad practices that it simply didn't matter. They successfully preserved compatibility in the nominal case. But even following all the best practices in your Python 2 code will not make it run under Python 3.
It seems to me like the primary difference that made the Python upgrade worse is that the authors of the big libraries in Ruby were enthusiastic about moving to Ruby 1.9, so that it was possible to start porting most Ruby apps very soon after 1.9 released, while a few big Python library authors dragged their feet on even getting started. This was a very real problem, but it was essentially a cultural problem of the library landscape being very different, not a matter of the language itself being drastically different.
>>> import codecs
>>> codecs.encode(b'eggs', 'hex')
b'65676773'
>>> codecs.encode(b'eggs', 'base64')
b'ZWdncw==\n'And while it isn't trivial, it is easy enough to just deal with bytes in 3.x.
import base64And so we are stuck, especially in enterprise where it could cost a lot of time and money to port because the transition wasn't managed well, both by users and the Python team.
I'm glad they did the "mediocre" fixes as well, they lead to a lot fewer encoding (and other) bugs, which were a real problem on nontrivial programs.
I didn't like how the changes to strings and bytes were made, but that's probably a larger discussion, and fits within how Py3 has evolved. If anyone is really confused by the struggle with adoption, just look through the release notes of version by version, and ask at what point there's a compelling reason to upgrade. I still don't see one, but at this point, nearly 10 years after Py3k, Py3.6 is finally usable enough that I wouldn't mind as much if I had to work on a Py3.6 codebase instead of a Py2.7 one.
I've found ordered dicts useful on a lot of projects, more than I'd have predicted in advance however.
nonlocal is another overdue fix.
Change has to be compelling. Python 3 wasn't until at least recently (3.5 and on).
Change has to be backwards compatible as possible. Python 3 wasn't, often for BS reasons.
Change better has some major selling feature. Python 3 has nothing that special over 2.
Change must come with nice conversion tools. 2to3 wasn't that.
etc...
People just can't be bothered to upgrade when it works well enough for them.
<rant> i think the real split is between CPython + C-Extensions and the JIT (PyPy,Numba) gang for heavy lifting in python. I for one wish Guido would embrace the effort of integrating the GILectomy into CPython and make python thread parrallism real for CPU bound work as opposed to the high overhead multiprocessing approach.
Also, including mypy in the stdlib to make genuine optional static typing would make python a defacto standard for much of what it does now without the "we love python but...." production code pain points. </rant>
The split you mention is real, but it's existed even longer than the 2vs3 split. All the various alternate Python implementations that have come and gone or are still around with the exception of Numba were started pre-3k. I wouldn't even argue that upstream was necessarily wrong not to prioritize performance and more specific use cases (like certain production uses, or scientific uses (a lot of begging to finally get the @ operator)), it just didn't matter as much for a long time. But things are different now. There are many other very expressive and much faster (either dynamically typed or static) languages in competition that lessen Python's effectiveness at reducing dev time in exchange for more hardware. Without a more serious focus on performance, Python will be driven only by momentum. That can last a long time, and is essentially the end point anyway when performance is "good enough" compared to the close alternatives, but it's not a great look when there's still much that could be done.