The transition from Python 2 into Python 3 was badly botched, and for many years. First of all, they confused language with implementation, so the Python 3 interpreter cannot handle the Python 2 language while the Python2 interpreter cannot handle the Python 3 language (I refer to the main C interpreter, not others like PyPy). The automated tool "2to3" wasn't reliable on real-world programs, either. For many years it was impractical to write programs or libraries that worked on both 2 and 3. To write any Python 3 code, ALL of your transitively-dependent libraries had to convert to Python 3, and effectively simultaneously. By itself, that was rediculous. In addition, Python 3 doesn't add any radically new capabilities over Python 2, so it was basically all pain and no gain. Python 3's new approach to strings is conceptually clean, but it struggles with the messiness of the real world and the Python 3 developers had to add a lot of hacks to make it sort-of work. For example, file names are not strings of characters on Unix/Linux, there is no standard encoding for stdin, and real-world data doesn't always follow any particular encoding and there's often no need to require it. Python 3 was effectively ignored by most programmers for at least 5 years after its release, because it created more problems than it solved.
What changed is that Python 3 was modified, and libraries were added to both Python 2 and 3, so that it started to became practical to write code that could work on both 2 and 3. The result: instead of having to transition your entire program and all its dependencies simultaneously, people could work incrementally, making little improvements here and there so that programs would keep working in 2, and eventually work in 3 as well. Various hacks were also created so that Python 3 could handle stuff like filenames (!).
It would have been much better had the Python 3 developers made it easier to transition from 2 to 3 from the start, but it's finally easier. It's still somewhat painful; mercurial still hasn't transitioned, and it's not clear that they ever will. I'm unhappy about what the Python developers did earlier, but I think they've made good strides in trying to finally make it much easier to transition from Python 2 to Python 3.
The main takeaway here should be, "make it as PAINLESS as POSSIBLE for your existing users to transition to a new version". It's okay to change APIs, but use different names or use versioned APIs. It's okay to change other things, but make it easy for people to modify parts of their program at a time. You'd think that'd be obvious, but it's clear that really smart people don't always think about that.
What changed is that Python 3 was modified, and libraries were added to both Python 2 and 3, so that it started to became practical to write code that could work on both 2 and 3. The result: instead of having to transition your entire program and all its dependencies simultaneously, people could work incrementally, making little improvements here and there so that programs would keep working in 2, and eventually work in 3 as well. Various hacks were also created so that Python 3 could handle stuff like filenames (!).
It would have been much better had the Python 3 developers made it easier to transition from 2 to 3 from the start, but it's finally easier. It's still somewhat painful; mercurial still hasn't transitioned, and it's not clear that they ever will. I'm unhappy about what the Python developers did earlier, but I think they've made good strides in trying to finally make it much easier to transition from Python 2 to Python 3.
The main takeaway here should be, "make it as PAINLESS as POSSIBLE for your existing users to transition to a new version". It's okay to change APIs, but use different names or use versioned APIs. It's okay to change other things, but make it easy for people to modify parts of their program at a time. You'd think that'd be obvious, but it's clear that really smart people don't always think about that.