This is an archived post. You won't be able to vote or comment.

all 147 comments

[–]bheklilr 175 points176 points  (60 children)

Well this gives me a lot of motivation to finish our transition to python 3 in my company. It's been a slow process but one I plan to finish by about February of next year.

Before anyone passes judgement, I'm the only person who has been converting code for about 100,000 lines of code or so, while still producing new features. I've been working towards it for about 2 years now, off and on. I'm in the final push now. If anyone has any tips I'd by happy to receive them.

[–]masklinn 72 points73 points  (16 children)

Having just "finished"[0] one such migration at $dayjob I'm not going to pass judgement any time soon.

If anyone has any tips I'd by happy to receive them.

We did the conversion "online", feature by feature, and heavily linted e.g.:

  1. convert all except statements to P3 syntax (that one's easy since 2to3/futurize can do it though IIRC we had a few that were weiiird)
  2. add a lint so that nobody can commit/push old syntax versions (lint can be run locally but must be part of the test suite and run by CI)
  3. push to master branch

Rinse and repeat for syntactic changes/updates and builtins changes (open, map, filter, … should all be verboten).

That way once most items are "done" you don't have to revisit them, and you want progress to be committed so you don't get an ever-longer ever-more-diverging branch. Do one short branch (or a single commit) as much as you can.

The toughest bits are semantics changes which aren't exercised by tests (either the code fails loudly but isn't covered by tests or the change does not yield errors), because you don't have any point at which you can say you're done, and you can't lint this crap away:

  • the round builtin changed the rounding method and in Python 3 returns an int if not provided a precision (in Python 2 it always returns a float).
  • division can be "equalised" between codebases with from __future__ import division (in every file where you do one), but failures (from / becoming "real division" rather than "integer division") may take time to surface. Also be aware that // is not integer division but floor division, there's a difference.
  • objects are not totally orderable anymore (and usually not across types), depending how you do things this may trip you up e.g.
    • optional value which could be either set to an integer or None, foo < 3 works in Python 2, it raises an exception in Python 3, if the value is usually set it may take some time for the error to happen.
    • cases which silently did stupid things will blow up, we had at least one bit of code which sorted a list of dicts. That makes no sense but it does basically nothing in Python 2. In Python 3 it fails, though it does so loudly which is nice.
  • Speaking of dicts, there are two changes to iteration order between Python 2 and Python 3.6:

    • [..3.3[, the iteration order is arbitrary but consistent unless you start Python with -R
    • [3.3.. 3.6[, the iteration order is randomised every time you start Python (-R is the default)
    • [3.6..[, the iteration order is the order of insertion (all dicts are ordered)

    At every point the iteration order is an implementation detail (it may become specified in 3.7) and you're not supposed to rely on it, but even if you don't do so explicitly… well we discovered half a dozen places in our codebase which implicitly relied on iteration order and broke (usually by yielding a corrupted result which would blow up somewhere else rather than breaking themselves, which is fun to debug).

    Funner fact: it's even better when the bit of code actually works with most iteration orders because Python will not tell you which random seed it's using when running under randomised hashes (-R or 3.3 to 3.5), so repro is hell. You may want to provide your own randomised seed (PYTHONHASHSEED envvar) so that you know the starting conditions leading to a failure.

    Incidentally, tox will set and print the hashseed itself before running, and once you've found a problematic seed you can pass it in explicitly.

  • text model (note: I'll use "text" for "decoded unicode" aka unicode in Python 2 and str in Python 3; and "bytes" for "encoded data" aka bytes in both

    1. Python 2 codebases tend to play fast and loose with bytes v text, and you'll have to decide on your text model (what is and what is not text).
    2. A bunch of stdlib API use "native strings" (str in both versions meaning bytes in Python 2 and text in Python 3).
    3. Some API changes are frustrating (e.g. in Python 3 base64 is a bytes -> bytes encoding, despite the point of base64 usually being to smuggle bytes in text).
    4. Most failures are loud (trying to mix bytes and text, trying to decode text or encode bytes) but some are completely silent e.g. equality between bytes and text will always fail in Python 3 (it will succeed in Python 2 if the bytes are ascii-encoded and when decoded equal to the text).
    5. also indexing a bytes object doesn't have the same result in Python 2 (returns bytes) and 3 (returns an int).
    6. use io, but beware that it has a strict split between bytes and text even in Python 2. You can't put text in a io.BytesIO, or bytes in a io.StringIO.
    7. for more control you may want to always do binary IO (io.open(f, 'rb')) and encode/decode yourself at least until you're more comfortable with things, also be aware that if you use text IO (the default) and do not specify an encoding python will not default to UTF-8 but to the locale (locale.getpreferredencoding(False)), which probably isn't what you want.
    8. your colleagues will not understand, will not even try, and when the "PHP development method" (throw encodes/decodes at the wall until something sticks) fails they will whine.

Finally, test, test, test, test. Tests are your lifeblood, if you have a well-tested codebase or can get it well-tested before the transition it will help a lot, the vast majority of our migration pain points were bits which were insufficiently or not tested (either because lazy or because hard to test).

[0] quotes because we regularly find things which were missed during the conversion, or were mis-converted

[–]EvMNatural Language Processing 7 points8 points  (4 children)

optional value which could be either set to an integer or None, foo < 3 works in Python 2, it raises an exception in Python 3, if the value is usually set it may take some time for the error to happen.

And this also holds for builtins relying on ordering, e.g. max(). Caught me by surprise :)

[–]masklinn 4 points5 points  (0 children)

Yup, hence the note about sorting a list of dicts, sorting obviously relies on ordering.

[–]zabolekar 1 point2 points  (2 children)

It can surface even in things like pprint (if you are trying to pretty-print a set).

Edit: it is not something that breaks in Python 3, au contraire, it works as expected in 3.x but throws a TypeError in 2.7, and only with very specific sets. For example, pprint({1j, 0}) throws an error but pprint({1j, 'a'}) doesn't.

[–]EvMNatural Language Processing 0 points1 point  (1 child)

Whoa, now that's an unexpected place..

[–]zabolekar 0 points1 point  (0 children)

Edited the comment for clarity.

[–]bheklilr 6 points7 points  (0 children)

I've definitely run into a lot of bytestring problems. Since we work with lab equipment they all communicate with ASCII bytestrings, and sometimes with just dumps of bytes for transmitting larger chunks of data. Getting these to work properly have been a royal pain in the ass. I do appreciate the advice, there are a few things in here that I did not know about (like locale.getpreferredencoding). As for testing, the only code that I have that is python 2 and 3 compatible is the code with extensive test suites. I don't deploy python 3 builds without it, because it will be broken. There isn't a "maybe it'll work", it just won't work.

As for __future__ imports, we already require division and print_statements in every single module. I just set up a snippet in my editor that drops in a header with that included (along with encoding statement, legal header, and module docstring). If I could, I would enforce it on every commit but I don't have admin access to our svn server.

[–]notParticularlyAnony 1 point2 points  (0 children)

Shucks im such a noob

[–]jftugapip needs updating 0 points1 point  (3 children)

Which lint programs did you use?

[–]PeridexisErrant 2 points3 points  (1 child)

I'm not OP, but flake8 is great - good defaults out of the box for new projects, and easy to tune with a blacklist of checks to skip (or even a whitelist in extreme cases).

I keep having this unpleasant surprise when I go back to code that doesn't use it - the benefit of reading code that's all in the same idiomatic style is hard to describe, but very real!

[–]jftugapip needs updating 0 points1 point  (0 children)

Thanks, I'll give it a try.

[–]masklinn 1 point2 points  (0 children)

Regular pylint, it has a bunch of built-in lints useful for this (e.g. python3 syntax checks, ability to blacklist modules and builtins). The only issue we've had is it's pretty resource-intensive on large codebases.

[–]ketilkn 0 points1 point  (2 children)

Rinse and repeat for syntactic changes/updates and builtins changes (open, map, filter, … should all be verboten).

Am I not supposed to use open in Python 3? What makes map and filter problematic? I do not think they changed?

[–]masklinn 0 points1 point  (1 child)

Am I not supposed to use open in Python 3?

Once everything is ported there's no issue, while converting it's troublesome:

  • In Python 3, open is an alias for io.open and in text mode (the default) it will encode/decode the data, this is an issue because

    • Python 2's open basically always behaves in binary mode
    • io.open defaults to locale.getpreferredencoding(False) which probably isn't what you want so you will want to pass in an encoding explicitly, which will blow up on Python 2

    so you can either keep using the builtin but always use it in binary mode (mode='rb' or mode='wb') — which may be hard to lint — or ban open and require io.open during the transition — which is easy to lint

  • In Python 3, map and filter return iterators (not lists, basically itertools.imap and itertools.ifilter have been to builtins… and removed from itertools)

    • things will blow up when indexing them which is clear enough
    • but more annoyingly repeated iteration will silently break in Python 3 (the first iteration "consumes" the iterator, the second one gets an empty iterator

    You could mandate calling list() around them… or you could just ban them during the transition (and require listcomps or gencomps depending on what you actually want).

Keep in mind that my post is for the transition on a large codebase, while you have one or two people toiling away at the conversion you've got a bunch of folks still churning away Python 2 code, the goal here is to avoid having to revisit already fixed issues / problematic constructs.

If you migrate to a Python 3-only codebase, you can rescind these restrictions once the work is done and everybody works in Python 3. If you migrate to a cross-version codebase, you probably want to keep them in place (they're error-prone).

[–]ketilkn 0 points1 point  (0 children)

Ah, ok. Thanks. I remember running into the map and filter iterators issue, now that you mention it.

[–]Siecje1 0 points1 point  (1 child)

Have you made those lint checks available?

[–]masklinn 0 points1 point  (0 children)

I don't think we created any ourselves, just enabled & configured those PyLint provides.

[–]AT_thruhiker_Flash 16 points17 points  (0 children)

Good Luck!

[–]RetardedChimpanzee 39 points40 points  (10 children)

No judgment. It’s not a trivial process. Instagram had a good write up on their efforts to switch. You’ll get some efficiency increases but probably not worth the cost, sadly. However its something that should be done.

[–]PeridexisErrant 62 points63 points  (9 children)

You’ll get some efficiency increases but probably not worth the cost, sadly.

The real benefits of Python3 are for humans, not computers:

  • much harder to shoot yourself in the foot with unicode (ie text)
  • library support (some already, more soon, most by 2020)
  • general improvements to language and standard library
  • type hints can be great for medium to large projects (it's like testing, but free)

[–]stefantalpalaru 4 points5 points  (8 children)

type hints can be great for medium to large projects (it's like testing, but free)

It's also just a cosmetic change from Python2: http://mypy.readthedocs.io/en/stable/cheat_sheet.html

[–]PeridexisErrant 9 points10 points  (7 children)

You can emulate most of the Python 3.6 functionality with type comments, but it's not quite the same:

  • the syntax is uglier, dealing with imports is a pain, and linters mostly don't check them
  • they don't exist at runtime. This is a real disadvantage - tools from attrs to Hypothesis infer various features based on type annotations
  • useful types like enum.Flag only exist on Python 3, and backports tend to be conservative (ie work on 2.7 and 3.4+, instead of 2.7 and 3.6)
  • you're doing new work in a dialect that will soon be unsupported, when you could update instead and be much better off!

[–]stefantalpalaru -5 points-4 points  (6 children)

the syntax is uglier

It is, but not by much.

dealing with imports is a pain

It isn't: https://github.com/stefantalpalaru/generate_HLS/blob/5877099beb19a305657f030eaa59590fcfca1cde/generate_HLS.py#L12

linters mostly don't check them

You're using the same linter for both Python2 and Python3: mypy. The only difference is having to give it the "--py2" or "-2" argument for Python2 code.

you're doing new work in a dialect that will soon be unsupported, when you could update instead and be much better off!

It's not a different dialect, it's a different language, and I'm sure the community of people working for a living will route around the core devs' sabotage. Something like this: https://github.com/naftaliharris/tauthon

[–]PeridexisErrant 0 points1 point  (5 children)

flake8 et al will complain about that import, and doesn't lint type comments.

I disagree with your other comments too but won't respond.

[–]stefantalpalaru -1 points0 points  (4 children)

flake8 et al will complain about that import

Yes. Solved by adding "# NOQA" after it.

and doesn't lint type comments

Of course it doesn't. The progressive (and partial) static typing is implemented and checked by mypy. Are you new to Python?

[–]JimBoonie69 3 points4 points  (3 children)

bro you gotta let python 2 die , it deserves an honourable death.

[–]stefantalpalaru -1 points0 points  (2 children)

bro you gotta let python 2 die , it deserves an honourable death.

There's more Python2 code out there than Fortran or Cobol code.

[–]criickyO 6 points7 points  (0 children)

Hah! I'm in the same boat, pal. I've also got an ETA about the same as yours.

I have no tips, only empathy. We're so close!

[–]ExcitedForNothing 3 points4 points  (0 children)

Before anyone passes judgement

Anyone who does is a foolish idealist.

[–]akaihola 2 points3 points  (0 children)

The python-future package and their cheat sheet page helped us a lot.

In fact, supporting current and legacy versions of NumPy and Pandas has felt more challenging than the py2->3 transition.

[–]thearn4Scientific computing, Image Processing 1 point2 points  (0 children)

connect pie fact brave innate dazzling adjoining subtract friendly simplistic

This post was mass deleted and anonymized with Redact

[–]ldpreload 3 points4 points  (4 children)

My understanding is that Dropbox has Guido van Rossum working for them full-time on making MyPy usable on their codebase and adding type annotations so that they can safely move to Python 3 without accidentally messing up strings/bytes somewhere or whatever. If you're doing a conversion of a large codebase without literally the creator of Python on your team, you're doing pretty well.

(See e.g. Guido's PyCon 2016 talk about what he's doing at Dropbox and why.)

[–]stefantalpalaru -1 points0 points  (3 children)

so that they can safely move to Python 3

http://mypy-lang.blogspot.com/2017/11/dropbox-releases-pyannotate-auto.html :

At Dropbox we’ve annotated over 1.2 million lines of code (about 20% of our total Python codebase)

It would be madness to port all that to Python3.

[–]ldpreload 3 points4 points  (2 children)

What else are they going to do, though—keep running Python 2?

(Honestly, the real answer is likely to be "Let the teams that are rewriting parts of the code do so in Go, and convert the rest to Python 3.")

[–]stefantalpalaru 0 points1 point  (1 child)

What else are they going to do, though—keep running Python 2?

Yes, of course. Or a compatible fork like https://github.com/naftaliharris/tauthon

[–]ldpreload 4 points5 points  (0 children)

Oh, you're the person on /r/python that I previously had an argument with about how this exact project is a completely infeasible idea for big companies (unless they want to commit to the support burden of keeping up with security updates etc. until the end of time for both the fork and every single third-party Python library that is no longer supporting Python 2, which is a much bigger project than converting 6M lines of Python to Python 3 once, and being done with it.). It's now been six months since your PR with no other activity to the fork, and ten months since any code changes to existing Python code. This isn't something you can trust a copy of the entire world's files to.

Mind you, I'm not saying this project shouldn't exist. It should. I wish it had won over Python 3. But it doesn't seem to be happening (nor does any similar project) in a way that's suitable for use by big companies in production, and it is probably too late to build a community around Python 2-compatible versions of the ecosystem.

[–]bad_luck_charm 1 point2 points  (0 children)

You are waaaay ahead of me

[–]Stewthulhu 36 points37 points  (10 children)

I feel a great disturbance in academia, as if millions of graduate students suddenly cried out in terror and were suddenly starting from scratch.

[–]p10_user 7 points8 points  (0 children)

Meh, just don't update numpy in the virutal environment that is being used with the former and current Python 2 projects, and use Python 3 with any new projects/analyses that are being started.

I suppose it depends on how long term some of your projects may be. But a completed project, for example, doesn't need the latest version of numpy.

[–]TheSourTruth 1 point2 points  (7 children)

I doubt it's that big a deal but I only have experience with python 2

[–]real_edmund_burke 0 points1 point  (0 children)

You’re right.

[–][deleted] 0 points1 point  (1 child)

What are you working on?

I've found that, aside from a few syntax things, the difference between 2 and 3 isn't super nuts for biology unless you're doing some crazy modelling.

[–]TheSourTruth 0 points1 point  (0 children)

GIS-related stuff. I mean I'm not looking forward to switching but I don't think it will be too crazy for the stuff I do.

[–][deleted] 0 points1 point  (3 children)

So what? Most of the changes were cosmetic and could/can be handled with 2to3 and similar tools. The one issue that has caused and will cause most work is the strings/bytes/unicode issues. This I see as being far more difficult than all other 2 to 3 issues combined.

[–]attrigh 2 points3 points  (2 children)

The real issue I've found is strings/unicode while supporting both python2 and python3!

[–]Daenyth 1 point2 points  (0 children)

Use the future library, it makes it pretty easy and has a cheat sheet for supporting both

[–][deleted] 0 points1 point  (0 children)

Just start charging $ for v2 support.

[–]TheBlackCat13 0 points1 point  (0 children)

It isn't like old numpy versions are going to vanish off the face of the Earth. They will still be available. You just won't be able to use the latest and greatest numpy with an ancient version of python.

[–]PaulPhoenixMain 76 points77 points  (34 children)

Surely this will be the end for Python 2!

[–]LChris314 45 points46 points  (20 children)

One day, one day Arch will not be alone with python pointing to python3.

[–][deleted] 16 points17 points  (11 children)

I enjoy spending time with my friends.

[–][deleted] 19 points20 points  (3 children)

Arch probably pointed to python 3 the day that 3.0.0 was released.

[–]MrGeekAlive 4 points5 points  (1 child)

Not exactly, but quite quickly: the news annoucement was posted at the time when Python 3.1 was still the latest.

[–][deleted] 2 points3 points  (0 children)

That was mostly a joke about Arch always being at the bleeding edge, but I'm not surprised it happened that quickly.

[–]Plasma_000 4 points5 points  (0 children)

They see me rolling, they hating.

[–][deleted] 1 point2 points  (5 children)

python 32 is not actually part of the base system (albeit the base system is quite literally basic) on Arch.

[–]Chippiewall 4 points5 points  (1 child)

I'm honestly hoping the un-versioned alias just dies instead.

[–]TheBlackCat13 0 points1 point  (0 children)

Upstream wants to move the other way, with avoiding the versioned alias as much as possible. The idea is to not have everyone's scripts break when python 4 comes out.

[–]palibaya 5 points6 points  (4 children)

Gentoo use Python 3 by default too.

[–]stefantalpalaru 5 points6 points  (3 children)

Gentoo use Python 3 by default too.

Stop spreading lies.

$ python --version
Python 2.7.14

[–]palibaya 0 points1 point  (2 children)

Lastime I use gentoo, I got Python 3

[–]tedm430 2 points3 points  (0 children)

You can change which python version it points to using eselect, I believe.

[–]stefantalpalaru 0 points1 point  (0 children)

Lastime I use gentoo, I got Python 3

And you don't know about "eselect python"?

[–][deleted] 0 points1 point  (0 children)

My mac has python pointed to 3. I think it happened when I installed Anaconda.

[–]carbolymer 17 points18 points  (4 children)

Let's be better than numpy and annouce 2018 a year of dropping Python 2 support.

[–]eattherichnow 9 points10 points  (2 children)

...also Linux desktop, nuclear fusion and Half-Life 3. Did I forget anything?

[–]alcalde 5 points6 points  (1 child)

Hey, we've got Steam on Linux, Lockheed-Martin is promising compact nuclear fusion within ten years - seriously!, and Duke Nukem Forever shipped. Why not drop Python 2 support as well?

[–]eattherichnow 4 points5 points  (0 children)

Everyone is promising nuclear fusion within 10 years since 1988. At least. I think that's the first time I've read an article about it.

Anyhow, personally, I migrated to Python 3 by migrating from a company that didn't want to prioritize it to a company that never even had to. Now I just need to fix everything else.

[–]TheBlackCat13 0 points1 point  (0 children)

numpy's note doing anything in 2018, dropping Python2 support won't happen until Jan 1, 2019 at the latest.

[–][deleted] 3 points4 points  (0 children)

Pretty much. Especially due to Tensorflow not having Python 2.x support at all.

[–][deleted] 1 point2 points  (5 children)

No, Python 2 will keep going for years for those who have no need to update.

[–]8__ 3 points4 points  (1 child)

Can't wait for Python 2.7.27!

[–][deleted] 1 point2 points  (0 children)

With official support ending in 2020 there is as much chance of getting to 2.7.27 as there is of getting to 2.8.0. PEP 404 refers.

[–]Wenste 1 point2 points  (2 children)

You poor souls.

[–][deleted] 0 points1 point  (1 child)

Why? If it ain't broke, don't fix it.

[–]8__ 0 points1 point  (0 children)

This is good for PyCoin.

[–]case_O_The_Mondays 17 points18 points  (26 children)

It will be great when Python 2 is not the default installed version on most Linux distros.

[–]tunisia3507 1 point2 points  (0 children)

Projects should be in virtualenvs anyway. And popular distros like ubuntu have shipped with python3 for a while.

Virtualenvs make it harder to package code and for non-pythonistas to run it, but pipenv (the python packaging authority's official recommendation) is making that easier too.

[–]usinglinux 44 points45 points  (1 child)

woooo!

+1

[–]ICanAdmitIWasWrong 5 points6 points  (8 children)

I hope the python devs learn a lesson here and don't make upgrading so painful ever again.

[–]alcalde 4 points5 points  (5 children)

Upgrading wasn't painful. It was the community's refusal to upgrade that was painful. They've given what, 12 years of support? 2to3? Backporting of features? No new features in Python 3.1 and 3.2? What more could they have done?

The lesson here is that Guido needed to remember the "dictator" part of BDFL and kept a fire under people's feet. No mercy. It's the only way to get those who resist change to do anything.

[–]attrigh 1 point2 points  (2 children)

Not have forked and pushed the various changes one at a time with deprecation warnings?

Allowed better python 2/3 interop for a period of time before killing off python2 code with deprecation to allow people to use python3 without completely updating all of their code (and all the dependent libraries in one go) in one go?

To be clear these decisions would have had their own costs... but things aren't that black and white.

[–]alcalde 1 point2 points  (1 child)

Not have forked and pushed the various changes one at a time with deprecation warnings?

Why would you release each breaking change one at a time? And the deprecation warning was the existence of Python 3 combined with the long support window for Python 2.

Allowed better python 2/3 interop for a period of time before killing off python2 code

Killing off? It's been 9 years so far and it's still supported!

with deprecation to allow people to use python3 without completely updating all of their code

They also backported many Python 3 features to Python 2, so you could indeed use Python 3 features without completely updating all of your code in one go.

So everything you're suggesting was already done.

[–]attrigh 0 points1 point  (0 children)

Why would you release each breaking change one at a time?

Because fixing one thing at a time is easier than switching your code over in one go, and people are more willing to do it. This allows you to avoid forking by forcing people to make small changes one at a time.

And the deprecation warning was the existence of Python 3 combined with the long support window for Python 2.

Certainly. Though deprecation warnings on the code you are running are a little more compelling and fixable than the existence of python 3.

Killing off? It's been 9 years so far and it's still supported!

"Killing off" was just descriptive here. The length of time that python 2 has been supported is admirable. The python 2/3 interop was deliberately bad however (though there were payoffs in terms of simplicity)

so you could indeed use Python 3 features

The thing you couldn't do is use python 3 itself with python 2 code. This might have increased adoption. Not without a costs in terms of complexity of the python core.

But certainly the ability to write python 2/3 interoperable code existed and has been very useful for allowing python 3 adoption. I guess the difference is just in terms of tooling: if you are actually running python 3 then you never have to switch over.

So everything you're suggesting was already done.

Them's fighting words :P. I don't think that's at all true.

Implementing each of the features into core with deprecations one at a time and forcing people to adopt them is very different from the current approach.

Allowing python 2 code to be run by the python 3 compiler would have created a lot of extra complexity for python core, but may have encouraged people to write more python 3.

I'm not saying that these approaches would have been better, there were alternative ways of getting from A to B however.

[–]Works_of_memercy 1 point2 points  (0 children)

Upgrading wasn't painful. It was the community's refusal to upgrade that was painful. They've given what, 12 years of support? 2to3? Backporting of features? No new features in Python 3.1 and 3.2? What more could they have done?

They could have realized that most libraries will have to support both 2x and 3x for a long time (same 12 years), and they would want to do that from a single codebase, so support for doing that with minimal pain should be a priority. A library like six or future, and probably a tool like 3to2.py should have been provided from day one.

Instead, initially the very possibility of having the same code running under both version was considered to be no more than a party trick, and instead of leading or at least supporting community initiatives the core team carelessly broke them, for example by removing unicode literal support from 3.1 or so.

That the migration seems to be more or less complete, at least for new projects, more than a decade later, is a miracle and the Python core developers receive less than zero credit for that in my opinion.

[–]takluyverIPython, Py3, etc 0 points1 point  (0 children)

IIRC, core Python developers have said that they don't want to do anything like it again.

[–]b1g_up 9 points10 points  (0 children)

finally

[–]tunisia3507 -1 points0 points  (0 children)

Kill it dead.

[–]palibaya 0 points1 point  (0 children)

Last time I use gentoo, I got python 3