Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Drop Python 2 support #1918

Merged
merged 12 commits into from
Aug 4, 2019
Merged

Drop Python 2 support #1918

merged 12 commits into from
Aug 4, 2019

Conversation

inclement
Copy link
Member

I propose dropping support for running under Python 2 after the current release is done.

I thought about adding an override command, but actually decided there isn't much point. Anyone who really wants to run under Python 2 can edit the code.

I think we should continue supporting the python2 recipe for now, since that's a smaller change and I don't perceive that it adds a big maintenance burden (at least in its current form, it was much worse before!).

@@ -100,6 +100,13 @@ def check_python_dependencies():
toolchain_dir = dirname(__file__)
sys.path.insert(0, join(toolchain_dir, "tools", "external"))

def check_python_version():
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

pythonforandroid/toolchain.py:103:1: E302 expected 2 blank lines, found 1

'Python 3 (recommended), or revert to python-for-android 2019.07.08. Note that '
'you *can* still target Python 2 on Android by including python2 in your requirements.')
if sys.version_info.major < 3:
raise BuildInterruptingException(py2_text)

def add_boolean_option(parser, names, no_names=None,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

pythonforandroid/toolchain.py:111:1: E302 expected 2 blank lines, found 1

AndreMiras
AndreMiras previously approved these changes Jul 14, 2019
Copy link
Member

@AndreMiras AndreMiras left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can't wait for it, thanks.
Linter unhappy btw

@opacam
Copy link
Member

opacam commented Jul 20, 2019

@inclement, as you said in #1883 comment, still we have an important point to decide,

Which python3 version should be the starting point?

Here are my 3 cents,

Despite Python 3.4+ is the minimal that we need (because some of our tests depends on this version), I would recommend to go a little further. My choice it would be Python 3.6+, mostly because f-strings where introduced in that version of python and I think that it is a great feature to make the code more readable.

Also it's important to mentioned this @AndreMiras comment: Ubuntu LTS doesn't have 3.5
This may not seem important, but it is, right now we rely on ubuntu for our docker images...and probably... a lot of p4a users are ubuntu users, so I think that this should be considered...

So, IMHO, I think that we should go for Python 3.6+

@inclement
Copy link
Member Author

I have no real objection to going 3.6+, but I'd like to check around if there are any hidden side effects. For instance, are there platforms we want to support where 3.6+ is not available, or even just less easy to access by default? I would think probably not, but I've been surprised before.

@ghost
Copy link

ghost commented Jul 28, 2019

I don't think personally that these formatting strings are important enough to jump to 3.6 just yet.

However, is there a reason we just add an error/warning at startup about Python 2 no longer being supported, but don't also remove the python 2 recipe and all the special handling for it? I think this could lead to a massive code complexity reduction for us

@inclement
Copy link
Member Author

@Jonast What minimum version would you go for? 3.4?

About removing the Python 2 recipe, I think it's nicer to do things this way because it's very simple (since the recipes and local version are totally decoupled) and it's a more user friendly step to make. We can remove the recipe at a future point. Also because I didn't think we had that much special handling for it any more, I would have pushed to remove it earlier if it had never been upgraded from the old one (which had special cases everywhere).

@inclement
Copy link
Member Author

I just updated this to be fairly complete.

Currently I put 3.6 as the minimum version, but only as a current default, without other input I would have gone for 3.4. I'm sure we might use various features of the newer versions if they were guaranteed to be present, but is there anything that people are specifically wanting other than f-strings in 3.6?

@ghost
Copy link

ghost commented Jul 28, 2019

Also because I didn't think we had that much special handling for it any more,

I think we have toooons of special handling for it, just think of the tox tests and so many things we aren't using in the overall codebase of p4a itself because a (host) Python 2 can't do it, like shlex.quote or super() without the ugly arguments. I think it would be a really great step forward to actually purge it, both for host usage and android target usage. But of course we don't need to do it right now, but I think we should do it eventually

@inclement
Copy link
Member Author

@Jonast Maybe I misunderstood, but allowing those things is the point of this PR - I'm proposing that from now on we support running python-for-android under 3.4+ only. That means we can move to Python3-only syntax in the p4a source.

For the android target, I'm thinking we may as well just set a hard deadline of 2020 and add a deprecation warning about it now when Python 2 target builds are used. I think it's worth giving a bit more notice on this one since it's much more of a feature change for users, whereas running p4a itself under Python 3 is a much more trivial update.

@ghost
Copy link

ghost commented Jul 28, 2019

@inclement some on-device code of the android module is tested in tox via the python 2 test target & mocking though, so we would lose some coverage if we dumped all build host python 2 usage for on-device target code. It's not massive though so we may just let that happen, but it is a bit intertwined

@inclement
Copy link
Member Author

@Jonast Ah, I see, I didn't realise that. I'm not that familiar with the tox config, but I think it would be possible to adjust it to only run the android module tests when run under Python 2, do you think that would be an okay intermediate solution?

@ghost
Copy link

ghost commented Jul 28, 2019

Yeah that should be possible, and sounds like a good start! I think it's the android module only anyway, so it's just a very small part of the tests

Edit: I just checked, and the concrete test file where this is relevant should be tests/test_androidmodule_ctypes_finder.py and possibly things in tests/recipes

@inclement
Copy link
Member Author

I've made an attempt to make the tox.ini run only the androidmodule test under Python 2.

Other than this I don't have much that I'm intending to do for this to be mergeable. I haven't updated the docs to say we're Python 3 only because I actually think that's kind of redundant, but I'll add a note about Python 2 deprecation in 2020 if everyone is okay with this plan.

There's still the question of minimum Python version. I've changed my mind to say we should be Python 3.4+, but I'm open to changing it again. The main advantages of going higher seem to be:

  • Python 3.5+ is actually working in our tests
  • Python 3.6+ gives us f-strings which seem to be popular
    I think the first of these is actually a pretty compelling reason go to with 3.5+ (did I remember that right, it's 3.5 and not 3.6?). I'm not averse to the idea that we can basically track what Ubuntu LTS supports.

@AndreMiras @opacam any thoughts?

@AndreMiras
Copy link
Member

Here're my 2 cents to advocate for 3.6.
Supporting 3.5 means testing against it. Who has the binary from core-dev & contributors?
How to install it in Travis which is running Ubuntu 18.04 LTS? It would probably need a ppa.
How many users are downloading 3.5 nowadays, response very few https://discuss.python.org/t/python-download-stats-for-march-2019/1424
image
But if we decide to go for 3.5 it's still fine as long as we ditch 2.7, I would still be very happy 😄

@ghost
Copy link

ghost commented Jul 28, 2019

I agree it may make sense to use something that is not too super recent, but still available without major pain in our testing environment. If that is 3.6 then this might not be the worst choice for that reason alone

AndreMiras
AndreMiras previously approved these changes Jul 28, 2019
Copy link
Member

@AndreMiras AndreMiras left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's do this!

from fnmatch import fnmatch
from tempfile import mkdtemp
try:

# This Python version workaround left for compatibility during initial version check
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice to have comments about this. Best would be some unit tests. I can deal with this later

@@ -17,6 +17,7 @@
data_files = []



Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nitpick: I hate spacing 😄

@inclement
Copy link
Member Author

Great, I'm convinced, 3.6+ it is.

@AndreMiras I'll add some tests before merging, I do want to be able to ensure that everything works under Python 2 up until the version check in the main entrypoint.

@inclement inclement added the WIP label Jul 29, 2019
@inclement inclement force-pushed the py3_only branch 2 times, most recently from ad080c4 to 173ee9e Compare August 2, 2019 23:08
from unittest.mock import MagicMock
except ImportError: # Python 2
import mock
from mock import MagicMock
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You guys are not lazy enough.
I would just go for import mock rather than always going with the try/except.
Basically the mock package is also installed for python3 https://github.com/kivy/python-for-android/blob/v2019.07.08/tox.ini#L7
Once we ditch Python2 completely, it's also easier to just search replace import mock to from unittest import mock or something like that.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It isn't installed on my desktop :p

I decided I preferred to be explicit about the Python 2 exception for the limited subset of files where we still need it. I'm willing to change it if you think it's better, but I consider this innocuous.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It doesn't need to be on your desktop since it's in the tox.ini.
It's definitely minor/preference so if you prefer explicit that's fine for me 👍

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would prefer keeping it in a way that works with the standard library (from unittest import mock). I personally don't mind these python 2 special branches because it reminds us that it needs to go 😄 if we change this to import mock nobody will remember to change it back, and I definitely think in the long term doing the right standard library import is better

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for sharing. I actually don't mind that much. I'm just being too lazy and I prefer one line over 4. Specially since it has to be repeated over a lot of (yet to come) tests files

tox.ini Outdated
@@ -28,7 +31,7 @@ commands = flake8 pythonforandroid/ tests/ ci/

[flake8]
ignore =
E123, E124, E126,
E123, E124, E126, E127, E129
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please not 🙏 I rather remove exceptions than adding them. Let's try to comply with the PEP8 even if it bother us

Copy link
Member Author

@inclement inclement Aug 3, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm complying with the "a foolish consistency is the hobgoblin of little minds" part :<

I have:

with mock.patch('sys.version_info') as fake_version_info, \
     mock.patch('pythonforandroid.entrypoints.handle_build_exception') as handle_build_exception:

Flake8 would prefer misalignment:

with mock.patch('sys.version_info') as fake_version_info, \
         mock.patch('pythonforandroid.entrypoints.handle_build_exception') as handle_build_exception:

I think this is plainly less consistent and readable.

In fact, my version turns out to be exactly the form given in pep8 as an example of how with statements are an exception to the normal recommendations:

with open('/path/to/some/file/you/want/to/read') as file_1, \
     open('/path/to/some/file/being/written', 'w') as file_2:
    file_2.write(file_1.read())

How about if I make this line a #noqa?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Interestingly, black makes a real mess of this:

    with mock.patch("sys.version_info") as fake_version_info, mock.patch(
        "pythonforandroid.entrypoints.handle_build_exception"
    ) as handle_build_exception:

I can't be the only one who finds this a much worse choice!

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for justifying it, yes it makes sense to me. I also hate the misalignment.
In fact I have two was to make it aligned while still complying with flake8. One involves blackslashes which @opacam suggested he didn't like, the other one involves parenthesis, which you suggested you didn't like 😄
I agree with your statement of foolish consistency, however in general enforcing consistency systematically brings more good than not having it.
Yes I would rather use a noqa too in this case, but I'm not sure how it behaves on multiple lines.
Yes I also think black sucks in this area, another reason I hate it. But also it would avoid us having these discussion and focus on other things, that's the reason I love it.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Well guys, I see more ways to this without relying on black... we can split lines in a lot of ways, this based on @AndreMiras solution in one of his recent PRs:

def mock_sys_info():
    return mock.patch('sys.version_info')


def mock_handle_exception():
    return mock.patch('pythonforandroid.entrypoints.handle_build_exception')


def test_main_python2():

    with mock_sys_info() as m_version, mock_handle_exception() as m_exception:

        m_version.major = 2
        m_version.minor = 7

        def check_python2_exception(exc):
            assert exc.message == PY2_ERROR_TEXT
        m_exception.side_effect = check_python2_exception

        entrypoints.main()

    m_exception.assert_called_once()

With this, you keep all the code below 80 characters, no noqa and using Context manager.

But, I would go with decorators...something like:

@mock.patch('pythonforandroid.entrypoints.handle_build_exception')
@mock.patch('sys.version_info')
def test_main_python2(fake_version_info, handle_build_exception):

    def check_python2_exception(exc):
        assert exc.message == PY2_ERROR_TEXT

    fake_version_info.major = 2
    fake_version_info.minor = 7
    handle_build_exception.side_effect = check_python2_exception

    entrypoints.main()

    handle_build_exception.assert_called_once()

Black it's great as an ortographic corrector and also to unify style of all the project even to find this kind of lines which doesn't fit as expected...then...IMHO... one must push the imagination a little further.

Play what you know and then play above that
Miles Davis

Copy link
Member

@AndreMiras AndreMiras left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is all good 👏
Only the fact we added E127 & E129 made me cry 😢

tox.ini Outdated
E124, # Closing bracket does not match visual indentation
E126, # Continuation line over-indented for hanging indent
E127, # Continuation line over-indented for visual indent
E129, # Visually indented line with same indent as next logical line
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice that you're adding the description for them all 👏
My review on this was pending and I forgot to submit.
Basically I was asking not to add E127 and E129 in the exception list since we've been successfully honored it until then.
I agree at first some PE8 feels annoying, but then we get used to and it definitely helps with consistency over a collaborative project like p4a

try: # Python 3
from unittest import mock
except ImportError: # Python 2
import mock
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Minor let's be lazy here too and just:

import mock

@inclement inclement removed the WIP label Aug 3, 2019
Copy link
Member

@AndreMiras AndreMiras left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's some very nice piece of work here 👏
Thanks @inclement!
Do you mind squashing in your commit merge since it's not being squashed in the PR?
We've been keeping a neat git history in lately in the recent PR, I think it's good if we keep it that way

@inclement inclement merged commit 40b1164 into kivy:develop Aug 4, 2019
@inclement
Copy link
Member Author

I'm not convinced that non-squash merges cannot be neat, but okay for now.

@inclement inclement deleted the py3_only branch August 4, 2019 16:43
@inclement
Copy link
Member Author

Thanks for the reviews!

@AndreMiras
Copy link
Member

Ahah thanks for making it even though you're not convinced, I really appreciate ☺️
I feel it's a bit chaotic without squashing. We had some linter exceptions enabled then disabled. Two commits for only styling. Many commits with broken builds. Untested feature first in. Python min version back and forth... For a 200 lines diff, I definitely think squashing makes things more clean and clear. This PR was 12 commits, if each PR were about the same number were squashed, our git history would have been 12 times as small. It also enforces to properly describe the feature as a whole I find. Popular & successful projects such as the Linux kerne, Django or many more also advocate for this practice as well as rebasing. See https://docs.djangoproject.com/en/2.2/internals/contributing/writing-code/working-with-git/ or https://www.linux.com/blog/how-git-your-first-contribution-open-source-community

@sujithkumar05
Copy link

guys, I am facing the same issue
can u help me out plz
am working on a project am using kivy with python it is showing python-for-android no longer supports running under python 2.either upgrade to python 3.4 or higher(recommended), or revert to python-for-android 2019.07.08.

@inclement
Copy link
Member Author

@sujithkumar05 If you don't understand some part of that error message, please ask about it on the Kivy discord or kivy-users mailing list.

@sujithkumar05
Copy link

sujithkumar05 commented Aug 26, 2019 via email

@AndreMiras
Copy link
Member

inclement added a commit to inclement/python-for-android that referenced this pull request Aug 31, 2019
This should have been the original value, agreed in
kivy#1918
inclement added a commit that referenced this pull request Feb 16, 2020
* Added missing recommendations command and cleaned up code

* Added test for print_recommendations running correctly

* Updated minimum Python version to 3.6

This should have been the original value, agreed in
#1918

* Added a blank line to make pylint happy

* Moved f-strings to format calls to preserve py2 compat in this file

* Changed travis build to use Python 3.8

* Undid travis change, for merging in a separate PR
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants