7 ms·
I have seen multiple people post to Ycombinator claiming that porting to Python3 is always trivial and easily done. And, yeah, sure, for a 380-line script writ
by strenholme 7y ago
I have seen multiple people post to Ycombinator claiming that porting to Python3 is always trivial and easily done. And, yeah, sure, for a 380-line script written after Python3 came out, porting the code so it runs the same both in Python2 and Python3 is trivial.
However, for an over 6000-line script written in the mid-2000s, before Python3 even existed, porting to Python3 is non trivial. Trust me, I tried.
First, the syntax sugar stuff was easy enough to fix: Do a simple search and replace so that all cases of stuff like
print "foo"
Become
print("foo")
I had to do something similar with the way this script calls raise(). Not to mention adding a hack so Python2 uses “xrange” while Python3 uses “range”. After doing all of this, the code still does not run in Python3. As far as I can tell, the issue is that Python3 does implicit float-to-int conversions differently than Python2; the code will not raise an error in Python3, but it will silently run differently, until the state is different enough that the Python3 version raises an error while the Python2 version works as expected.
At this point, I just threw my hands in the air and gave up.
For reference, the offending code is here:
https://github.com/samboy/misc-civ4-mapscripts/blob/master/TotestraRG32.py https://github.com/samboy/misc-civ4-mapscripts/blob/master/T...
- raverbashing 7y agoWell, yes, porting large projects to Py3 is not trivial. That's why they had, what, around 5 years to do it since the 2.7 EOL was extended to 2020? I think most of the problems you encountered would be solved by the six library, btw. But the most annoying bugs were those were some Python 2 quirk was "just working" (like comparing strings to integers in Python 2) or the conversion of ints and floats and then it correctly broke on Python 3.
- strenholme 7y agoUsing the six library is one way to make a large project being written from the ground up run in both Python2 and Python3. For this particular project, I would had needed to get the code to run in both Python3 and an ancient version of Python2 made back when boost.python was still a thing, so six would be out of the question.
- MapleWalnut 7y agoHave you tried https://docs.python.org/3.9/library/2to3.html https://docs.python.org/3.9/library/2to3.html? It should automate some of the syntax conversation. You can also write custom transforms with 2to3 but I've found https://pybowler.io https://pybowler.io is a decent wrapper. I haven't used it for 2to3 conversion though.
- deckar01 7y agoNo one has claimed porting to python 3 is easy in this thread. That said, if you start by "search and replace", then you are probably doing it wrong [0]. [0]: https://docs.python.org/2/library/2to3.html https://docs.python.org/2/library/2to3.html
- newen 7y agoThere are lots and lots of people who have claimed that porting to python 3 is easy. Stop twisting the truth.
- finnthehuman 7y agoHacker News gets some of the smartest people willing to jump into a comment section, but its also full of uninformed idiots talking out of their ass and trying to sound authoritative. I won’t defend them, but complaining about something someone else posted in another thread isn’t gonna get you too much sympathy either.
- newen 7y agoYeah, complaining. It's irritating when idiots try to "win" arguments in the internet by spouting completely obvious bullshit.
- deckar01 7y agoOP changed their comment to clarify they were referring to HN discussions historically and not just this thread. I can't edit my comment now, and I apologize for the confusion. I would encourage you to review the YC comment guidelines [0] and engage in more thoughtful discussion in the future. [0]: https://news.ycombinator.com/newsguidelines.html https://news.ycombinator.com/newsguidelines.html
- traverseda 7y ago>Python3 does implicit float-to-int conversions differently I don't think so? Might be floating point division. > In Python 3.0, 5 / 2 will return 2.5 and 5 // 2 will return 2. The former is floating point division, and the latter is floor division, sometimes also called integer division. https://stackoverflow.com/questions/183853/what-is-the-difference-between-and-when-used-for-division https://stackoverflow.com/questions/183853/what-is-the-diffe... I think an automated tool like `2to3` is probably a better bet for doing that kind of conversion. It should be a mostly automatic process for most scripts.
- opencl 7y ago2to3 does NOT handle the change in division behavior. There is no automated way to do so because python2 only does floor division sometimes and it's decided at runtime based on the arguments being ints or not.
- fiddlerwoaroof 7y agoI believe you can get python3 behavior on a file by file basis with from __future__ import division But it’s been a while since I’ve written anything serious in python.
- opencl 7y agoYes you can, but this person needs the python2 behavior in python3 which pretty much requires selectively replacing / with //.
- epylar 7y agoYep, you can use // to get python2 behavior unless the object redefines __truediv__ but not __floordiv__
- mumblemumble 7y agoThat works for writing new code in Python 2.7. It would also have worked for going the recommended route of gradually migrating toward Python 3 compatibility while remaining on Python 2, _provided_ you're working with an organized project that's amenable to converting on a file-by-file basis. I can see where it falls flat on its face when you're trying to migrate a monolithic 6000-line script. But, IMO, that example falls flat on its face when used as a rhetorical device. If you've got a monolithic 6000-line script with (presumably) no test coverage, you've made your own bed, and I don't think that the inherent difficulties of maintaining something like that should be blamed exclusively on the 2->3 transition.
- deleted 7y ago[deleted]
- rlpb 7y agoNobody is saying otherwise, but maintaining EOL software is also hard. The Debian project does have any able developers volunteering to do it for Python 2 in Debian's next release. That's all that there is to it.
- belorn 7y agoThe primary choices for distribution are to either demand maintainers that they updates their scripts, or to take up maintaining the python 2.7 code base themselves, a code base which is written in C and mostly before 2000. Is your code in Debian right now, or was your comment a general question in regard to the status of python 2.7?
- shadowgovt 7y agoOut of curiosity, how is this issue handled for languages like Algol or Fortran? The gfortran codebase was also written significantly in C and mostly before 2000.
- basilgohar 7y agoI've read elsewhere in this thread that those implementations are still being maintained, so they are not in the same situation ad Python 2.
- dwheeler 7y agoThe 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.
- diminoten 7y agoThe number of atrocities committed in that code are mind numbing -- this is what you get for writing such unmaintainable code.
- mturmon 7y agoCould have been said more kindly, but the point is accurate. This reads like C code that happens to be written in Python. There is a lot of fiddly bit-twiddling operations. The main issue — besides not using 2to3 — is that / in Python3 is interpreted as floating point division, whereas it used to be integer division if operands were integers. Replacing / by // in the relevant code blocks might help? Or getting another implementation of the RNG?
- misnome 7y agoIt really isn't that bad. I regularly see much, much worse, and much more unidiomatic python, from people who have gone on to work at FAANG.
- strenholme 7y agoThe code base in question is not one I wrote; it is one I decided to maintain years after the original author lost interest in it. [1] This is not too different from what I have seen in multiple jobs, where the new hire doesn’t get to write all the code they use from scratch, but instead has to maintain someone else’s mess. Often times, management and market conditions demand more from programmers than they have time to properly implement with test cases, well structured code, good comments, etc. Technical debt is a big problem in code used by businesses, and the technical debt this particular 6000-line script has is a lot less than some stuff I have seen and have been paid to maintain. The fact of the matter is that most open source code out there is quite a bit cleaner and maintainable than the code which is behind firewalls, running important businesses. [1] Edit: To the original author’s credit, this particular code base is an incredibly popular and successful map generator for Civilization 4, with some 40 thousand downloads; it got messy because the author was more interested in adding new features requested by users than in keeping the code base clean. It is a single script because that made it a lot easier for users to install. And, newer versions of the same generator are written in Lua and not Python because that is what newer versions of Civilization use for scripting.
- djsumdog 7y agoIt's so vitally important to write tests. Years ago I started doing full tests suites, even for my open source, work, once it got mature enough I felt comfortable with the patterns I established. Then I'd add tests before developing new features. Unit tests help make upgrading so much easier. I was once on a huge Scala codebase that needed to go up from Spray.io to Akka-HTTP. Tests help save me on tons of potential bugs in migrating over. The few things that got through, I could write more tests for. On another open source project I maintain, my functional tests helped me migrate from Scala 2.11 to 2.12 along with all my dependencies with relatively low friction. If you're porting your Python2 to Python3 stuff now, it's never too late to start writing some tests. Write them over the weird division stuff you've found. Make sure they run in Py2 and fail in Py3 so you can isolate what it is. Even a couple of small tests in a large code base that only cover stuff you're finding now is better than no tests at all.
- aeturnum 7y agoThere is no sure-er mark of a Python novice than the "2 to 3 conversion is trivial" myth. Python has a long history, there are lots of versions of 2, there are complicated data processing changes from all versions of 2 to all versions of 3. Not every conversion is hard, but they can be an enormous amount of work.
- nabdab 7y ago> After doing all of this You hopefully mean after spending 1 minute running py2to3 right? I mean I get that some people feel that it’s tedious or difficult to convert if they spend ages manually changing thousands of lines of code in a completely automatable fashion. Which is why it’s nice that you don’t have to actually do that. You just run py2to3 and the focus on differences that matter.
- kerkeslager 7y agodef a91a15d7(x): Jesus. I didn't read much farther than this, but it does not surprise me that this codebase is difficult to work on.
- kerkeslager 7y agoI ported a website written in 2009 from Python 2 to Python 3, which was around 20,000 lines of code. It wasn't "trivial", but it took me (a single developer) a little less than a week. Most of the difficulty wasn't code changes, but upgrading dependencies.
- CivBase 7y agoDevil's advocate: Many people aren't even bothered to try. The company I work for still writes new stuff in Python _2.7.3_ - not even a recent release of 2.7. Some of their stuff will be difficult to convert, but a lot of it really is trivial. They just don't care to put any effort into it until things start to break down. I can understand why people get frustrated by situations like this and are tempted to call out programs still running Python 2 and those who advocate for keeping it. I'm a huge fan of some of the features introduced by Python 3, so I switched and am not interested in going back. However, I think there is room for other interpreters to step in and continue supporting Python 2 even if the PSF wont.
- londons_explore 7y agoGoogle?
- strenholme 7y agoIf I were in that situation, I would make a reasonable effort to keep scripts as polyglots: Make sure they both run in Python 2 and in Python 3. In my experience, doing so is pretty easy to do when writing new code from scratch with Python 3 compatibility in mind. The “no one is maintaining Python 2” issue is an example of open source economics in action: Everyone wants Python2 to be maintained, but no one is stepping up to plate to pay for it to be maintained, so it’s not getting maintained.
- Izkata 7y ago> Python _2.7.3_ Familiar number. Maybe they tried, and stuff broke in weird and unexpected ways, so they gave up. For example, the first time we upgraded from that to (I think) .8, lots of strange errors started coming in from our webapp. Apparently kombu, one of the dependencies of celery, was importing an underscore-function from the standard library, which no longer existed in the new version.
- CivBase 7y agoThis is definitely the case for a few apps. It's why I raise hell anytime I see someone use a private attribute or function from a builtin module. However, I'm painfully aware that it does not apply to most of our apps and it does not justify using an outdated interpreter for writing new apps.
- munificent 7y agoMaybe it's not trivial, but if you'd migrated two lines of code a day, you'd be done by now. Code requires some amount of maintainance.
- deleted 7y ago[deleted]
- mixmastamyk 7y agoIt is often easy, but not always. Write some tests around the problematic functionality before attempting the final port, it should help in this situation. Should be broken into multiple files as well.
- archi42 7y agoRecently I had to port some code from the legacy version of an internal DSL to our newer version. I just wrote a primitive parser that would understand 98% percent of the code (in a safe way) and lowered that into the new DSL. Worked like a charm, and I didn't have to rewrite ~2000000 lines (a colleague tried pattern matching, but that was unusable for complex expressions). I wonder if/why there is no such ~~thing~~ [edit] effort for python2/3? If you're only accepting programs that P2 accepts, you can even get away with being lazy and "understanding" more than P2.
- Izkata 7y agoThis isn't from experience (I have yet to do a conversion myself), but one thing I've heard is that the str/unicode change revealed that a lot of codebases didn't understand the difference in py2, were using the wrong one at API boundaries, and required a lot of manual work to figure out what it should be for each one so py3 could handle it.
- archi42 7y agoOuch. GP mentioned int/float conversion differences, and coders using the wrong [string/unicode] types doesn't help. I wonder if these are symptoms of duck typing? Edit: 2to3 is what I'm talking about https://docs.python.org/2/library/2to3.html https://docs.python.org/2/library/2to3.html
- hermitdev 7y agoPython doesn't implicitly conver floats to in ints. As others have commented, there have been changes to how division is handled in that int / int may result in a float. But, to the beat of my knowledge, there's no implicit float -> int conversion. Maybe. Some libs convert floats to into, but its not the norm.
- pdonis 7y ago> the way this script calls raise() That actually isn't a function call, despite the appearance. Python 3 raise is still a statement, not a function (unlike print). So this raise(ValueError, "some text") does not do what you think; the proper way in Python 3 to spell what you probably expect is raise ValueError("some text") Here is a transcript of a shell session showing the difference: Python 3.5.2 (default, Oct 8 2019, 13:06:37) [GCC 5.4.0 20160609] on linux Type "help", "copyright", "credits" or "license" for more information. >>> raise ValueError("some text") Traceback (most recent call last): File "<stdin>", line 1, in <module> ValueError: some text >>> raise(ValueError, "some text") Traceback (most recent call last): File "<stdin>", line 1, in <module> TypeError: exceptions must derive from BaseException Note that the exception you actually get the second time isn't even ValueError, it's TypeError; it's being raised because "some text" is a string, not an instance of an exception class derived from BaseException. In other words, you should not be treating raise in Python 3 the way print is treated. The 2to3 tool treats it properly.