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 support for Python 2 #1764

Merged
merged 1 commit into from
Sep 4, 2018
Merged

Drop support for Python 2 #1764

merged 1 commit into from
Sep 4, 2018

Conversation

hugovk
Copy link
Contributor

@hugovk hugovk commented Apr 28, 2018

Copy link
Collaborator

@tilgovi tilgovi left a comment

Choose a reason for hiding this comment

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

Looks good!

@tilgovi
Copy link
Collaborator

tilgovi commented Jul 30, 2018

One more change we can do: I think we can get rid of six.py. We can replace six.reraise with just raise and six.exec_ with the exec built-in.

Copy link
Collaborator

@berkerpeksag berkerpeksag left a comment

Choose a reason for hiding this comment

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

I think all % to str.format() changes need to be reverted. There's nothing wrong with using %-formatting and I don't think it's worth introducing code churn.

I can say the same thing for reordering imports but I don't have a strong objection.

@@ -39,7 +39,7 @@ def format_settings(app):
known_settings = sorted(guncfg.KNOWN_SETTINGS, key=lambda s: s.section)
for i, s in enumerate(known_settings):
if i == 0 or s.section != known_settings[i - 1].section:
ret.append("%s\n%s\n\n" % (s.section, "-" * len(s.section)))
ret.append("{}\n{}\n\n".format(s.section, "-" * len(s.section)))
Copy link
Collaborator

@berkerpeksag berkerpeksag Jul 30, 2018

Choose a reason for hiding this comment

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

%-formatting will stay forever in Python, so I don't think these changes are in the scope of this PR.

Copy link
Owner

Choose a reason for hiding this comment

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

i"m also not sure i really like it, it makes it a little hard to parse for a human. I would keep the %s which is also quite similar to other languages

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done by reverting 159aa1e.

@@ -340,7 +334,7 @@ def validate_dict(val):


def validate_pos_int(val):
if not isinstance(val, six.integer_types):
if not isinstance(val, int,):
Copy link
Collaborator

Choose a reason for hiding this comment

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

int, -> int

Copy link
Contributor Author

Choose a reason for hiding this comment

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

CONFIG_DEFAULTS = dict(
version=1,
disable_existing_loggers=False,
CONFIG_DEFAULTS = {
Copy link
Collaborator

Choose a reason for hiding this comment

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

The dict(...) version is pretty readable. I don't think we should do cosmetic-only changes.

Copy link
Owner

Choose a reason for hiding this comment

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

agree

Copy link
Contributor Author

@hugovk hugovk Jul 31, 2018

Choose a reason for hiding this comment

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

✅ by reverting 4aa3a5e. (It's not only cosmetic, it's faster too: https://stackoverflow.com/a/6612024/724176. But this might not be important here, and let's keep the one you find more readable.)


def read(self, size):
if not isinstance(size, six.integer_types):
if not isinstance(size, int,):
Copy link
Collaborator

Choose a reason for hiding this comment

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

int, -> int

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@@ -110,7 +112,7 @@ def __init__(self, unreader, length):
self.length = length

def read(self, size):
if not isinstance(size, six.integer_types):
if not isinstance(size, int,):
Copy link
Collaborator

Choose a reason for hiding this comment

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

int, -> int

Copy link
Contributor Author

Choose a reason for hiding this comment

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

return six.MAXSIZE
elif not isinstance(size, six.integer_types):
return sys.maxsize
elif not isinstance(size, int,):
Copy link
Collaborator

Choose a reason for hiding this comment

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

int, -> int

Copy link
Contributor Author

Choose a reason for hiding this comment

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


def chunk(self):
raise NotImplementedError()

def read(self, size=None):
if size is not None and not isinstance(size, six.integer_types):
if size is not None and not isinstance(size, int,):
Copy link
Collaborator

Choose a reason for hiding this comment

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

int, -> int

Copy link
Contributor Author

Choose a reason for hiding this comment

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

from gunicorn import util
from gunicorn.workers.workertmp import WorkerTmp
from gunicorn.http.errors import (ForbiddenProxyRequest, InvalidHeader,
Copy link
Collaborator

@berkerpeksag berkerpeksag Jul 30, 2018

Choose a reason for hiding this comment

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

The current style is much better and it makes future diffs less noisy:

from gunicorn.http.errors import (
    Spam, Eggs,
)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

from . import base
from .. import six

from .. import http, six, util
Copy link
Collaborator

Choose a reason for hiding this comment

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

Can six be removed here?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

gunicorn/util.py Outdated
@@ -155,10 +154,6 @@ def set_owner_process(uid, gid, initgroups=False):
except KeyError:
initgroups = False

# versions of python < 2.6.2 don't manage unsigned int for
# groups like on osx or fedora
gid = abs(gid) & 0x7FFFFFFF
Copy link
Owner

Choose a reason for hiding this comment

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

has this change been checked? fedora do some odd things sometimes...

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I've not checked it so have reverted it to be on the safe side.

Copy link
Owner

@benoitc benoitc left a comment

Choose a reason for hiding this comment

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

Thanks for the PR!

I have for now only reviewed the code (see the comments) and I still have to test it but these changes looks good.

Maybe a little nitpicking, but I am not sure about the string format changes, I am thinking the current way is more readable especially for those who have to switch languages . Maybe that could be discussed in a separate change? Thoughts?

gunicorn/util.py Outdated
@@ -317,7 +311,7 @@ def write_nonblock(sock, data, chunked=False):


def write_error(sock, status_int, reason, mesg):
html = textwrap.dedent("""\
markup = textwrap.dedent("""\
Copy link
Owner

Choose a reason for hiding this comment

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

i would make it more explicit, like html_error or just respbody

Copy link
Contributor Author

Choose a reason for hiding this comment

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

from . import base
from .. import six

from .. import http, six, util
Copy link
Owner

Choose a reason for hiding this comment

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

please keep it on multiple lines, it's easier to read that way

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@@ -39,7 +39,7 @@ def format_settings(app):
known_settings = sorted(guncfg.KNOWN_SETTINGS, key=lambda s: s.section)
for i, s in enumerate(known_settings):
if i == 0 or s.section != known_settings[i - 1].section:
ret.append("%s\n%s\n\n" % (s.section, "-" * len(s.section)))
ret.append("{}\n{}\n\n".format(s.section, "-" * len(s.section)))
Copy link
Owner

Choose a reason for hiding this comment

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

i"m also not sure i really like it, it makes it a little hard to parse for a human. I would keep the %s which is also quite similar to other languages

@@ -45,10 +43,10 @@ class Arbiter(object):
SIG_QUEUE = []
SIGNALS = [getattr(signal, "SIG%s" % x)
for x in "HUP QUIT INT TERM TTIN TTOU USR1 USR2 WINCH".split()]
SIG_NAMES = dict(
(getattr(signal, name), name[3:].lower()) for name in dir(signal)
SIG_NAMES = {
Copy link
Owner

Choose a reason for hiding this comment

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

i would have kept the dict( .. ) syntax there, it was easier to parse for human eyes

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done by reverting 159aa1e.

inspect.Parameter.POSITIONAL_OR_KEYWORD,
)

def get_arity(f):
Copy link
Owner

Choose a reason for hiding this comment

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

should be in gunicorn.util

Copy link
Contributor Author

Choose a reason for hiding this comment

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

CONFIG_DEFAULTS = dict(
version=1,
disable_existing_loggers=False,
CONFIG_DEFAULTS = {
Copy link
Owner

Choose a reason for hiding this comment

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

agree

@benoitc benoitc self-assigned this Jul 31, 2018
@hugovk
Copy link
Contributor Author

hugovk commented Jul 31, 2018

Thanks for the reviews!

I've updated the PR as requested, please let me know if I've missed anything.

There's now a linting error:

gunicorn/http/wsgi.py:236:20: E0702: Raising NoneType while only classes or instances are allowed (raising-bad-type)

How should six.reraise(exc_info[0], exc_info[1], exc_info[2]) be updated?

@@ -234,7 +233,7 @@ def start_response(self, status, headers, exc_info=None):
if exc_info:
try:
if self.status and self.headers_sent:
reraise(exc_info[0], exc_info[1], exc_info[2])
raise exc_info
Copy link
Collaborator

Choose a reason for hiding this comment

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

The wsgiref module does this:

        if exc_info:
            try:
                if self.headers_sent:
                    # Re-raise original exception if headers sent
                    raise exc_info[0](exc_info[1]).with_traceback(exc_info[2])
            finally:
                exc_info = None        # avoid dangling circular ref

https://github.com/python/cpython/blob/master/Lib/wsgiref/handlers.py#L215-L221

six's implementation is more defensive:

https://github.com/benjaminp/six/blob/master/six.py#L687-L696

We can copy six's implementation into gunicorn/util.py.

Copy link
Collaborator

Choose a reason for hiding this comment

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

And note that reraise() from six ignores exc_type. This would cause a problem if we want to raise the exception with a different exception type, but since we no longer support Python 2, we can do the same thing with raise new from old.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

gunicorn/util.py Outdated
return urlsplit(uri)


def reraise(tp, value, tb=None):
Copy link
Collaborator

Choose a reason for hiding this comment

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

Nit: Can you add a comment to note that we borrow this code from six?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@@ -188,7 +187,7 @@ def handle_request(self, listener, req, client, addr):
respiter.close()
except EnvironmentError:
# pass to next try-except level
six.reraise(*sys.exc_info())
raise sys.exc_info()
Copy link
Collaborator

Choose a reason for hiding this comment

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

sys.exc_info() returns a tuple. I think using a plain raise would be enough here. Also, I'm not sure we still need the comment # pass to next try-except level though. @benoitc what do you think?

Copy link
Collaborator

Choose a reason for hiding this comment

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

Also, EnvironmentError is an alias of OSError since Python 3.3. It might be better if we replace it with OSError. @benoitc @tilgovi What do you think?

@@ -20,13 +20,11 @@
'Operating System :: MacOS :: MacOS X',
'Operating System :: POSIX',
'Programming Language :: Python',
'Programming Language :: Python :: 2',
'Programming Language :: Python :: 2.6',
'Programming Language :: Python :: 2.7',
'Programming Language :: Python :: 3',
'Programming Language :: Python :: 3.4',
'Programming Language :: Python :: 3.5',
'Programming Language :: Python :: 3.6',
Copy link
Collaborator

Choose a reason for hiding this comment

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

Nit: Can you add 3.7 as well?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@berkerpeksag
Copy link
Collaborator

Please delete gunicorn/http/_sendfile.py and replace its usages with os.sendfile(). os.sendfile() was added in Python 3.3, so we no longer need the compatibility layer.

@tilgovi
Copy link
Collaborator

tilgovi commented Jul 31, 2018

Thanks for all the detailed reviews! Still looking good to me.

@berkerpeksag
Copy link
Collaborator

Unfortunately, there are still a few raise sys.exc_info() calls left in gunicorn/workers/*. I think they need to be replaced either with a bare raise or gunicorn.util.reraise():

>>> import sys
>>> try:
...   1/0
... except Exception:
...   raise sys.exc_info()
... 
Traceback (most recent call last):
  File "<stdin>", line 2, in <module>
ZeroDivisionError: division by zero

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "<stdin>", line 4, in <module>
TypeError: exceptions must derive from BaseException

Unfortunately, I don't have time to make a review and decide which one is the best this week :(

@hugovk
Copy link
Contributor Author

hugovk commented Aug 1, 2018

Thanks, I've replaced them with util.reraise(*sys.exc_info()) to avoid any unintended consequences of changing functionality.

import urllib
unquote_to_wsgi_str = urllib.unquote
def unquote_to_wsgi_str(string):
return urllib.parse.unquote_to_bytes(string).decode('latin-1')


# The following code adapted from trollius.py33_exceptions
Copy link
Collaborator

Choose a reason for hiding this comment

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

_wrap_error() can also be deleted.

return exec(code, *args)


def bytes_to_str(b):
Copy link
Collaborator

@berkerpeksag berkerpeksag Aug 1, 2018

Choose a reason for hiding this comment

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

We can now move bytes_to_str() and unquote_to_wsgi_str() to gunicorn.util since they no longer behave differently in Python 2 and Python 3.

execfile_() and its friends can stay here for now. There was a discussion about deprecating PYC support in 20 and rewriting it with importlib (instead of deprecated imp) is a complex task.

else:
module = '__builtin__'

module = 'builtins'
Copy link
Collaborator

Choose a reason for hiding this comment

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

Style nit:

return 'builtins.{}'.format(name)

@berkerpeksag
Copy link
Collaborator

This looks pretty good to me, thanks!

One more request: Could you squash all commits into one and add the following Co-Authored-By markers to the commit message?

Co-Authored-By: Dustin Ingram <di@users.noreply.github.com>
Co-Authored-By: Berker Peksag <berker.peksag@gmail.com>

Co-Authored-By: Dustin Ingram <di@users.noreply.github.com>
Co-Authored-By: Berker Peksag <berker.peksag@gmail.com>
@hugovk
Copy link
Contributor Author

hugovk commented Aug 1, 2018

Done! How's that look?

@berkerpeksag
Copy link
Collaborator

Perfect, thanks! :)

@benoitc
Copy link
Owner

benoitc commented Aug 2, 2018

Thanks, I will test later today with the use cases from the example folder. Can others d the same and report the result there also?

cc @tilgovi @berkerpeksag

@benoitc
Copy link
Owner

benoitc commented Aug 17, 2018

tests seems OK. If no news I will do a merge next week on monday. master needs to be frozen until then (cc @tilgovi @berkerpeksag )

@berkerpeksag
Copy link
Collaborator

Ah, I forgot to left a comment here. I played with the examples and use this branch in my project without any problems. I think this is good to merge.

@berkerpeksag
Copy link
Collaborator

@benoitc let me know if you want me to do the merging if you don't have time to do it :)

@benoitc
Copy link
Owner

benoitc commented Sep 2, 2018

i'm still testing it in a somewhat loaded platform. I will take care of the merge next week :)

@benoitc benoitc merged commit e974f30 into benoitc:master Sep 4, 2018
@hugovk hugovk deleted the rm-2 branch September 4, 2018 10:21
@hugovk hugovk mentioned this pull request Sep 4, 2018
@benoitc
Copy link
Owner

benoitc commented Sep 4, 2018

PR has been merged (not sure why it doesn't appear in closed in PR). @hugovk thanks for it and thanks all for the reviews.

In addition to the PR I also fixed examples that didn't pass in python3.

@hugovk
Copy link
Contributor Author

hugovk commented Sep 4, 2018

You're welcome!

This was referenced Sep 4, 2018
berkerpeksag pushed a commit that referenced this pull request Sep 4, 2018
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