The Python 2 -> 3 transition has been
unusually bad.
In most programming languages, a change to the language is practically always backwards-compatible. The update may add new capabilities, but the old capabilities and syntax remain in place. If the language changes semantics, it's usually in bizarre edge cases of the language. If a basic data type was a mistake, for example, a new data type is added & people are encouraged to use it instead... but the old one keeps working. If an existing data type or method should be renamed, the new name is provided & the old name keeps working for a long time so that people can incrementally update.
In short, the normal expectation in programming language updates is that existing code will keep working, and that updates can be done slowly and incrementally. That's historically been true for Python as well; updates typically didn't break much. In the few cases where there were significant backwards-incompatible changes, languages typically give 10+ years of warning.
The Python 2 -> 3 transition has instead been an example in how to do things badly. Pretty much no Python 2-only code will work without change; you have to change print to print(), division's semantics have changed, string semantics have changed, common names have changed (xrange to range), and so on. The original automated transition tool originally envisioned could never work reliably, because Python's dynamic nature ensures that there's not enough information to work without help. So the demands on people to make changes were unusually large for a language update.
A related problem was that there was never an implementation that supported Python2 and Python3 at the same time. That meant that if you depended on 100 Python2 libraries, you had to wait for all of them to transition before you could transition. If 99.99% of the libraries you depend on are available in Python3, you must use Python2. You can easily create C or Fortran programs that call on code written long ago unchanged; that is not true for Python3.
So in the Python3 transition all code had to be modified simultaneously across the ecosystem, and tools couldn't do it well.
Eventually the Python3 folks made enough changes to Python3 so that it started becoming practical to write code that worked on both Python2 and Python3. That should have happened from the beginning.
The only language I can think of that handled a transition worse is Visual Basic. When "Visual Basic for .NET" came out, it was so grossly incompatible with the previous Visual Basic that it was called "Visual Fred". In numbers I saw long ago, about 1/3 stayed with the old unsupported Visual Basic, 1/3 changed to a completely different language (that was less likely to abuse them), and only 1/3 of the previous Visual Basic users transitioned to Visual Basic for .NET.
I think that Python3, all by itself, is a fine language. The problem in this case is that the developers of Python made it unnecessarily hard to transition to Python3. That was both unfortunate and unnecessary.