10000 bpo-29654 : Support If-Modified-Since HTTP header (browser cache) by PierreQuentel · Pull Request #298 · python/cpython · GitHub
[go: up one dir, main page]

Skip to content

bpo-29654 : Support If-Modified-Since HTTP header (browser cache) #298

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

Merged
merged 32 commits into from
Apr 2, 2017
Merged

bpo-29654 : Support If-Modified-Since HTTP header (browser cache) #298

merged 32 commits into from
Apr 2, 2017

Conversation

PierreQuentel
Copy link
Contributor

The PR adds support of the If-Modified-Since HTTP request header.

If the user agent sends this header and the url matches a file that was not modified after the value specified in the header, the server returns status code 304 (Not Modified).

A test for this feature (test_browser_cache) is added in test_httpservers.py.

@the-knights-who-say-ni
Copy link

Hello, and thanks for your contribution!

I'm a bot set up to make sure that the project can legally accept your contribution by verifying you have signed the PSF contributor agreement (CLA).

Unfortunately we couldn't find an account corresponding to your GitHub username on bugs.python.org (b.p.o) to verify you have signed the CLA. This is necessary for legal reasons before we can look at your contribution. Please follow these steps to help rectify the issue:

  1. If you don't have an account on b.p.o, please create one
  2. Make sure your GitHub username is listed in "Your Details" at b.p.o
  3. If you have not already done so, please sign the PSF contributor agreement. The "bugs.python.org username " requested by the form is the "Login name" field under "Your Details".
  4. If you just signed the CLA, please wait at least one US business day and then check "Your Details" on bugs.python.org to see if your account has been marked as having signed the CLA (the delay is due to a person having to manually check your signed CLA)
  5. Reply here saying you have completed the above steps

Thanks again to your contribution and we look forward to looking at it!

Copy link
Member
@serhiy-storchaka serhiy-storchaka left a comment

Choose a reason for hiding this comment

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

Please sign the PSF contributor agreement and add entries in Misc/NEWS and What's New file.

if ims is not None:
ims_datetime = datetime.datetime(*ims[:7])
ims_dtstring = ims_datetime.strftime("%d %b %Y %H:%M:%S")
last_modif = datetime.datetime.utcfromtimestamp(
Copy link
Member

Choose a reason for hiding this comment

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

ims_datetime is naive datetime object, but datetime.datetime.utcfromtimestamp() creates aware datetime object.

ims_dtstring = ims_datetime.strftime("%d %b %Y %H:%M:%S")
last_modif = datetime.datetime.utcfromtimestamp(
fs.st_mtime).strftime("%d %b %Y %H:%M:%S")
if last_modif <= ims_dtstring:
Copy link
Member

Choose a reason for hiding this comment

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

Why compare string representations rather than datetime objects or just tuples? The order of such strings is different from the order of datetime objects.

@@ -1212,3 +1228,5 @@ def test(HandlerClass=BaseHTTPRequestHandler,
else:
handler_class = SimpleHTTPRequestHandler
test(HandlerClass=handler_class, port=args.port, bind=args.bind)

Copy link
Member

Choose a reason for hiding this comment

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

Unrelated changes.

# Use browser cache if possible
if "If-Modified-Since" in self.headers:
# compare If-Modified-Since and date of last file modification
fs = os.stat(path)
Copy link
Member

Choose a reason for hiding this comment

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

Use os.fstat().

Copy link
Member

Choose a reason for hiding this comment

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

Maybe combine with the existing call below, too

@serhiy-storchaka serhiy-storchaka added the type-feature A feature request or enhancement label Feb 26, 2017
@@ -687,6 +688,21 @@ def send_head(self):
self.send_error(HTTPStatus.NOT_FOUND, "File not found")
< 10000 span class="blob-code-inner blob-code-marker-context"> return None
try:
# Use browser cache if possible
if "If-Modified-Since" in self.headers:
Copy link
Member

Choose a reason for hiding this comment

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

The RFC https://tools.ietf.org/html/rfc7232#section-3.3 says you have to ignore this field if If-None-Match is also specified

if "If-Modified-Since" in self.headers:
# compare If-Modified-Since and date of last file modification
fs = os.stat(path)
ims = email.utils.parsedate(self.headers["If-Modified-Since"])
Copy link
Member

Choose a reason for hiding this comment

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

This email function accepts a timezone (e.g. from the documentation: -0500), and seems to ignore it. But it may make more sense for a HTTP server to either adjust the date according to the timezone (e.g. add 5 hours), or treat it as an error (required by RFC 7232).

#constructs the path relative to the root directory of the HTTPServer
response = self.request(self.base_url + '/test')
self.check_status_and_reason(response, HTTPStatus.OK, data=self.data)
# get Last-Modified response heaer
Copy link
Member

Choose a reason for hiding this comment

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

Spelling: header. But this comment seems redundant; isn’t the code obvious enough?

headers = Message()
headers['If-Modified-Since'] = last_modif
response = self.request(self.base_url + '/test', headers=headers)
self.check_status_and_reason(response, HTTPStatus.NOT_MODIFIED)
Copy link
Member

Choose a reason for hiding this comment

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

Please repeat the test with an earlier date, verifying that a response body is returned.

# Use browser cache if possible
if "If-Modified-Since" in self.headers:
# compare If-Modified-Since and date of last file modification
fs = os.stat(path)
Copy link
Member

Choose a reason for hiding this comment

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

Maybe combine with the existing call below, too

@PierreQuentel
Copy link
Contributor Author

Thanks for all the feedback.

I have signed the CLA, it will probably be approved tomorrow. I will add the entries in Misc/NEWS and What's New.

In the second commit :

  • the datetime comparison now uses datetime objects in the UTC timezone, not their string representation. The time of file last modification must be rounded to the second because If-Modified-Since is also rounded
  • if a If-None-Match header is present, If-Modified-Since is ignored - but at the moment, If-None-Match is not handled
  • tests have been added

try:
# Use browser cache if possible
if "If-Modified-Since" in self.headers:
if "If-Modified-Since" in self.headers \
Copy link
Member

Choose a reason for hiding this comment

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

It is more preferable to use parenthesis rather than line continuation.

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

try:
# Use browser cache if possible
if "If-Modified-Since" in self.headers:
if "If-Modified-Since" in self.headers \
and not "If-None-Match" in self.headers:
Copy link
Member

Choose a reason for hiding this comment

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

"If-None-Match" not in self.headers

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 too

fs.st_mtime).strftime("%d %b %Y %H:%M:%S")
if last_modif <= ims_dtstring:
# If-Modified-Since is UTC, rounded to the second
tzinfo = datetime.timezone(datetime.timedelta(hours=0))
Copy link
Member

Choose a reason for hiding this comment

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

datetime.timezone.utc

But maybe use naive datetime objects?

# compare If-Modified-Since and date of last file modification
fs = os.stat(path)
ims = email.utils.parsedate(self.headers["If-Modified-Since"])
Copy link
Member

Choose a reason for hiding this comment

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

It looks a problem to me that email.utils.parsedate() silently ignores timezone. Maybe use email.utils.parsedate_to_datetime() instead?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Ok, the latest commit uses parsedate_to_datetime() and checks that it has a timezone set to UTC, which should be the case for the header (cf RFC 7231). To compare with last modification time, both must be timezone-aware datetimes.

import email.utils
dt = email.utils.parsedate(last_modif)
# build datetime object : one year before last modification
old_dt = [dt[0] - 1] + list(dt[1:7])
Copy link
Member

Choose a reason for hiding this comment

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

Assuming the date reflects when the test suite is run, this will fail on a leap day (e.g. 2020-02-29). Perhaps try something like “datetime(*old_dt) - timedelta(days=365)”.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Correct, I have modified the test.

ims_datetime = datetime.datetime(*ims[:7], tzinfo=tzinfo)
# compare to UTC datetime of last modification, also
# rounded to the second
mtime = int(fs.st_mtime)
Copy link
Member

Choose a reason for hiding this comment

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

“Int” rounds towards 1970. The actual rounding that “date_time_string” uses seems to be a combination of rounding half to even microseconds (thanks to datetime), and then rounding down (always backwards in time) to seconds.

It might be more sensible to call datetime.replace(microseconds=0).

self.check_status_and_reason(response, HTTPStatus.OK)

# build datetime object : one hours after last modification
new_dt = [dt[0]] + [dt[1] + 1] + list(dt[2:7])
Copy link
Member

Choose a reason for hiding this comment

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

Dt[1] is attribute tm_mon, but the comment suggests you want to adjust by one hour.

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 changed the implementation with timedelta(hours=1) instead

@@ -110,6 +110,12 @@ urllib.parse
adding `~` to the set of characters that is never quoted by default.
(Contributed by Christian Theune and Ratnadeep Debnath in :issue:`16285`.)

http.server
Copy link
Member

Choose a reason for hiding this comment

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

Move before unittest.mock. Modules should be lexicographically ordered.

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 in commit 143e1e9

self.send_response(HTTPStatus.NOT_MODIFIED)
self.end_headers()
f.close()
return
Copy link
Member

Choose a reason for hiding this comment

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

return None. If there are return statements with value, all None values should be returned explicitly.

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

# compare If-Modified-Since and time of last file modification
ims = email.utils.parsedate_to_datetime(
self.headers["If-Modified-Since"])
if (ims is not None and
Copy link
Member

Choose a reason for hiding this comment

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

This can be written in one line.

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 in c3337f4

@@ -686,10 +687,30 @@ def send_head(self):
except OSError:
self.send_error(HTTPStatus.NOT_FOUND, "File not found")
return None

fs = os.fstat(f.fileno())
Copy link
Member

Choose a reason for hiding this comment

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

f is leaked if os.fstat() raises an exception.

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

if ("If-Modified-Since" in self.headers
and "If-None-Match" not in self.headers):
# compare If-Modified-Since and time of last file modification
ims = email.utils.parsedate_to_datetime(
Copy link
Member

Choose a reason for hiding this comment

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

email.utils.parsedate_to_datetime() can raise an exception. It might be better just to ignore the cache rather than return an error.

Needed a cache for ill-formed "If-Modified-Since" value.

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 in c3337f4

try:
# Use browser cache if possible
if ("If-Modified-Since" in self.headers
Copy link
Member

Choose a reason for hiding this comment

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

HTTP headers are case-insensitive. Does this work with "if-modified-since"?

Copy link
Member

Choose a reason for hiding this comment

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

Self.headers should be (similar to) an email.message.Message object, so I think it should work fine

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 confirm that it's case-insensitive

ims = email.utils.parsedate_to_datetime(
self.headers["If-Modified-Since"])
if (ims is not None and
ims.tzinfo is datetime.timezone.utc):
Copy link
Member

Choose a reason for hiding this comment

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

Maybe you should also allow tzinfo to be None, to support the obsolete “asctime” format from https://tools.ietf.org/html/rfc7231#section-7.1.1.1 without a timezone (e.g. Sun Nov 6 08:49:37 1994)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

This should not happen often, but it is supported in commit c3337f4

try:
# Use browser cache if possible
if ("If-Modified-Since" in self.headers
Copy link
Member

Choose a reason for hiding this comment

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

Self.headers should be (similar to) an email.message.Message object, so I think it should work fine

try:
ims = email.utils.parsedate_to_datetime(
self.headers["If-Modified-Since"])
except:
Copy link
Member

Choose a reason for hiding this comment

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

Never use bare except unless you reraise the exception. This hides such exceptions as KeyboardInterrupt or MemoryError.

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 added the try block because you said parsedate_to_datetime() could raise exceptions ; do you know which ones ? By looking at the code I see that it might raise TypeError because _parsedate_tz may return None and the line

*dtuple, tz = _parsedate_tz(data)

would raise a TypeError in this case ; but is it the only one ?

Anyway, the code is itself inside a try...except block where the except block is just except:, so this is not making things much worse.

Copy link
Member
@vadmium vadmium Feb 26, 2017

Choose a reason for hiding this comment

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

That TypeError looks like a bug in the implementation. Other exceptions:

>>> parsedate_to_datetime("Sun, 99 Feb 2017 20:49:31")
ValueError: day is out of range for month
>>> parsedate_to_datetime("Sun, 26 Feb 2017 20:49:31 +12345678901234")
OverflowError: Python int too large to convert to C long
>>> parsedate_to_datetime("Mon, 20 Nov , 19:12:08 GMT")
IndexError: string index out of range

The point is the outer exception handler reraises the exception, but your handler swallows it.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Thanks, the exceptions have been specified in commit 1e68be9

except:
# ignore ill-formed values
ims = None
if ims is not None:
Copy link
Member

Choose a reason for hiding this comment

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

Can email.utils.parsedate_to_datetime() return None? If it can't, just use else.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

email.utils.parsedate_to_datetime() either returns a datetime.datetime() or raises an exception, in which case ims is set to None.

@@ -444,6 +444,45 @@ def test_head(self):
self.assertEqual(response.getheader('content-type'),
'application/octet-stream')

def test_browser_cache(self):
Copy link
Member

Choose a reason for hiding this comment

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

I think it would be better to split this test on few tests.

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 in commit 42edfe3.
The datetime of last modification is stored in method setUp and used in the browser cache tests. I also added a test for the Last-Modified header.

self.check_status_and_reason(response, HTTPStatus.OK)

# with If-Modified-Since earlier than Last-Modified, must return 200
import datetime
Copy link
Member

Choose a reason for hiding this comment

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

Import at module level. Importing inside functions is used in exceptional cases.

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 in commit 42edfe3

@@ -110,7 +117,6 @@ urllib.parse
adding `~` to the set of characters that is never quoted by default.
(Contributed by Christian Theune and Ratnadeep Debnath in :issue:`16285`.)


Copy link
Member

Choose a reason for hiding this comment

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

Unrelated change.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Sorry, I don't understand what I should do here

Copy link
Member

Choose a reason for hiding this comment

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

You removed an empty line. Insert it back.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Ok, done in commit 9436e45

except (TypeError, IndexError, OverflowError, ValueError):
# ignore ill-formed values
ims = None
if ims is not None:
Copy link
Member

Choose a reason for hiding this comment

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

Just else:

except (TypeError, IndexError, OverflowError, ValueError):
    # ignore ill-formed values
    pass
else:

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 in commit fc20596

@@ -1212,3 +1243,4 @@ def test(HandlerClass=BaseHTTPRequestHandler,
else:
handler_class = SimpleHTTPRequestHandler
test(HandlerClass=handler_class, port=args.port, bind=args.bind)

Copy link
Member

Choose a reason for hiding this comment

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

Don't add this line.

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 in commit fc20596

@PierreQuentel
Copy link
Contributor Author

Is there something left to do before the pull request can be merged ?

@@ -96,6 +96,13 @@ New Modules
Improved Modules
================

http.server
-----------
:class:`SimpleHTTPRequestHandler` supports the HTTP If-Modified-Since header.
Copy link
Member

Choose a reason for hiding this comment

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

:class:`~http.server.SimpleHTTPRequestHandler`

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 in 6a58894

Misc/NEWS Outdated
@@ -666,6 +666,9 @@ Library
- Issue #24142: Reading a corrupt config file left configparser in an
invalid state. Original patch by Florian Höch.

- bpo-29654 : Support If-Modified-Since HTTP header (browser cache). Patch
Copy link
Member

Choose a reason for hiding this comment

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

Remove the space before colon. Use two spaces after sentence ending period. Move the entry to the start of the "Library" section (new entries are added at the start).

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 in 6a58894

@@ -100,6 +100,7 @@
import socketserver
import sys
import time
import datetime
Copy link
Member

Choose a reason for hiding this comment

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

Try to support alphabetical order of imports. I.e. add import datetime before import email.utils.

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 in commit 708ff03. The order was not supported for argparse and copy...
I also changed to version to 0.7.

@@ -17,8 +17,11 @@
import urllib.parse
import html
import http.client
import email.message
Copy link
Member

Choose a reason for hiding this comment

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

Try to support alphabetical order of imports.

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 don't understand. In the current version, the imports are not in alphabetic order :

from http.server import BaseHTTPRequestHandler, HTTPServer, \
     SimpleHTTPRequestHandler, CGIHTTPRequestHandler
from http import server, HTTPStatus

import os
import sys
import re
import base64
import ntpath
import shutil
import urllib.parse
import html
import http.client
import tempfile
import time
from io import BytesIO

import unittest
from test import support

There doesn't seem to be any logical order either - http.client is not grouped with http.server for instance.

Would you mind explaining more what you want and why ? It will be helpful for next Pull Requests.

Copy link
Member

Choose a reason for hiding this comment

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

Just place import email.utils the first and import datetime after it. You can also move import base64 before them.

Copy link
Member

Choose a reason for hiding this comment

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

This is just a style nit, you shouldn't follow it if you have other reasons.

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 changed the order in commit db46671. I tried several orders but couldn't find something obvious. I prefer to leave Internet-related modules together, and time and datetime together.

... and thanks for the expression "style nit", I didn't know it :-)

10000

@PierreQuentel
Copy link
Contributor Author

Is there still anything to do ?

@serhiy-storchaka serhiy-storchaka requested a review from vadmium March 5, 2017 16:02
Copy link
Member
@serhiy-storchaka serhiy-storchaka left a comment

Choose a reason for hiding this comment

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

LGTM, but maybe @vadmium have other comments.

Copy link
Member
@vadmium vadmium left a comment

Choose a reason for hiding this comment

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

The code changes look okay in general, and I support the idea of the change. I haven’t actually tested this out though (and am unlikely to do so for a while). Just left a couple minor thoughts.

@@ -80,13 +80,16 @@
# (Actually, the latter is only true if you know the server configuration
# at the time the request was made!)

__version__ = "0.6"
__version__ = "0.7"
Copy link
Member

Choose a reason for hiding this comment

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

I don’t understand the __version__ change. This is roughly documented as affecting SimpleHTTPRequestHandler.server_version, and the Server response header field. But the current 0.6 value has existed since 2001: revision 077153e. It seems safer to leave it alone unless we know what the numbers mean etc.

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 in 6dadf6e

-----------
:class:`~http.server.SimpleHTTPRequestHandler` supports the HTTP
If-Modified-Since header. The server returns the 304 response status if the
target file was not modified after the datetime specified in the header.
Copy link
Member

Choose a reason for hiding this comment

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

Would it be better to just say “after the time specified in the header”? Datetime is just a Python implementation detail, not an English word or part of HTTP.

Also, I wonder if the details should go in the main documentation. Already https://docs.python.org/dev/library/http.server.html#http.server.SimpleHTTPRequestHandler.do_GET has quite a few details of the server behaviour.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Replaced "datetime" by "time" in c389bf6

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Update of the http.server documentation proposed in 294e164

@serhiy-storchaka
Copy link
Member

Do you still have comments @vadmium?

@vadmium
Copy link
Member
vadmium commented Mar 23, 2017

Only other thing that I wonder about is if the documentation should say the Not-Modified handling was added in 3.7 (usually with “.. versionchanged::” markup). But if other people don’t think this needs to be added, I guess it doesn’t matter that much.

@PierreQuentel
Copy link
Contributor Author

I think it's worth mentioning, for instance if a test suite expects a status 200 and gets 304 in version 3.7. It's not a very likely use case, but it may happen. I have added the "Changed in version 3.7" comment in c8b108f.

@PierreQuentel
Copy link
Contributor Author

Can you merge the PR if there is nothing else to do ?

@serhiy-storchaka
Copy link
Member

The Travis CI build failed: trailing whitespaces in http.server.rst.

@PierreQuentel
Copy link
Contributor Author

Fixed in 16f9e11, all checks pass.

@serhiy-storchaka serhiy-storchaka merged commit 351adda into python:master Apr 2, 2017
@PierreQuentel
Copy link
Contributor Author

@serhiy-storchaka @vadmium
Thank you for your patience, it was my first PR for this project and I missed things that are obvious to you.

akruis pushed a commit to akruis/cpython that referenced this pull request Aug 4, 2021
The was a race, when extracting libstackman.a.
akruis pushed a commit to akruis/cpython that referenced this pull request Aug 8, 2021
Kristján pointed me to another race, but now extracting the archive
members should be save.
jaraco pushed a commit that referenced this pull request Dec 2, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
type-feature A feature request or enhancement
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants
0