diff options
| -rw-r--r-- | .hgtags | 2 | ||||
| -rw-r--r-- | CHANGES.rst | 18 | ||||
| -rwxr-xr-x | setup.cfg | 2 | ||||
| -rwxr-xr-x | setup.py | 2 | ||||
| -rw-r--r-- | setuptools/command/test.py | 21 | ||||
| -rw-r--r-- | setuptools/command/upload_docs.py | 88 | ||||
| -rwxr-xr-x | setuptools/package_index.py | 9 | ||||
| -rw-r--r-- | setuptools/tests/test_upload_docs.py | 12 |
8 files changed, 104 insertions, 50 deletions
@@ -265,3 +265,5 @@ ddd3f81eb9e0860bf95c380c50a72c52a215231f v21.0.0 40b8fac6db119aca9c462993d01908492769fc4f v21.2.0 9959424676a4aac1c14e430ff6f4210fdb0442d9 v21.2.0 694111eadb10fe6003078895a2cbb803ce514ef2 v21.2.1 +274f33435e9c3ba5019f2a2bfe478fa2db0da41d v21.2.2 +451fbedb4c226d8ea5b6eab1e21679c9a4ec4a93 v22.0.0 diff --git a/CHANGES.rst b/CHANGES.rst index b6a7ba0f..77edc67b 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -2,6 +2,22 @@ CHANGES ======= +v22.0.0 +------- + +Intended to be v21.3.0, but jaraco accidentally released as +a major bump. + +* #598: Setuptools now lists itself first in the User-Agent + for web requests, better following the guidelines in + `RFC 7231 + <https://tools.ietf.org/html/rfc7231#section-5.5.3>`_. + +v21.2.2 +------- + +* Minor fixes to changelog and docs. + v21.2.1 ------- @@ -109,7 +125,7 @@ v20.6.0 20.5 ---- -* BB Pull Request #185: Add support for environment markers +* BB Pull Request #185, #470: Add support for environment markers in requirements in install_requires, setup_requires, tests_require as well as adding a test for the existing extra_requires machinery. @@ -1,5 +1,5 @@ [bumpversion] -current_version = 21.2.1 +current_version = 22.0.0 commit = True tag = True @@ -68,7 +68,7 @@ wheel = ['wheel'] if needs_wheel else [] setup_params = dict( name="setuptools", - version="21.2.1", + version="22.0.0", description="Easily download, build, install, upgrade, and uninstall " "Python packages", author="Python Packaging Authority", diff --git a/setuptools/command/test.py b/setuptools/command/test.py index 371e913b..39746a02 100644 --- a/setuptools/command/test.py +++ b/setuptools/command/test.py @@ -1,6 +1,7 @@ +import sys +import contextlib from distutils.errors import DistutilsOptionError from unittest import TestLoader -import sys from setuptools.extern import six from setuptools.extern.six.moves import map @@ -102,6 +103,14 @@ class test(Command): yield self.test_suite def with_project_on_sys_path(self, func): + """ + Backward compatibility for project_on_sys_path context. + """ + with self.project_on_sys_path(): + func() + + @contextlib.contextmanager + def project_on_sys_path(self): with_2to3 = six.PY3 and getattr(self.distribution, 'use_2to3', False) if with_2to3: @@ -137,7 +146,7 @@ class test(Command): working_set.__init__() add_activation_listener(lambda dist: dist.activate()) require('%s==%s' % (ei_cmd.egg_name, ei_cmd.egg_version)) - func() + yield finally: sys.path[:] = old_path sys.modules.clear() @@ -154,9 +163,11 @@ class test(Command): cmd = ' '.join(self._argv) if self.dry_run: self.announce('skipping "%s" (dry run)' % cmd) - else: - self.announce('running "%s"' % cmd) - self.with_project_on_sys_path(self.run_tests) + return + + self.announce('running "%s"' % cmd) + with self.project_on_sys_path(): + self.run_tests() def run_tests(self): # Purge modules under test from sys.modules. The test loader will diff --git a/setuptools/command/upload_docs.py b/setuptools/command/upload_docs.py index f887b47e..01b49046 100644 --- a/setuptools/command/upload_docs.py +++ b/setuptools/command/upload_docs.py @@ -13,6 +13,8 @@ import socket import zipfile import tempfile import shutil +import itertools +import functools from setuptools.extern import six from setuptools.extern.six.moves import http_client, urllib @@ -21,15 +23,9 @@ from pkg_resources import iter_entry_points from .upload import upload -errors = 'surrogateescape' if six.PY3 else 'strict' - - -# This is not just a replacement for byte literals -# but works as a general purpose encoder -def b(s, encoding='utf-8'): - if isinstance(s, six.text_type): - return s.encode(encoding, errors) - return s +def _encode(s): + errors = 'surrogateescape' if six.PY3 else 'strict' + return s.encode('utf-8', errors) class upload_docs(upload): @@ -101,10 +97,48 @@ class upload_docs(upload): finally: shutil.rmtree(tmp_dir) + @staticmethod + def _build_part(item, sep_boundary): + key, values = item + title = '\nContent-Disposition: form-data; name="%s"' % key + # handle multiple entries for the same name + if not isinstance(values, list): + values = [values] + for value in values: + if type(value) is tuple: + title += '; filename="%s"' % value[0] + value = value[1] + else: + value = _encode(value) + yield sep_boundary + yield _encode(title) + yield b"\n\n" + yield value + if value and value[-1:] == b'\r': + yield b'\n' # write an extra newline (lurve Macs) + + @classmethod + def _build_multipart(cls, data): + """ + Build up the MIME payload for the POST data + """ + boundary = b'--------------GHSKFJDLGDS7543FJKLFHRE75642756743254' + sep_boundary = b'\n--' + boundary + end_boundary = sep_boundary + b'--' + end_items = end_boundary, b"\n", + builder = functools.partial( + cls._build_part, + sep_boundary=sep_boundary, + ) + part_groups = map(builder, data.items()) + parts = itertools.chain.from_iterable(part_groups) + body_items = itertools.chain(parts, end_items) + content_type = 'multipart/form-data; boundary=%s' % boundary + return b''.join(body_items), content_type + def upload_file(self, filename): - f = open(filename, 'rb') - content = f.read() - f.close() + with open(filename, 'rb') as f: + content = f.read() meta = self.distribution.metadata data = { ':action': 'doc_upload', @@ -112,37 +146,13 @@ class upload_docs(upload): 'content': (os.path.basename(filename), content), } # set up the authentication - credentials = b(self.username + ':' + self.password) + credentials = _encode(self.username + ':' + self.password) credentials = standard_b64encode(credentials) if six.PY3: credentials = credentials.decode('ascii') auth = "Basic " + credentials - # Build up the MIME payload for the POST data - boundary = '--------------GHSKFJDLGDS7543FJKLFHRE75642756743254' - sep_boundary = b('\n--') + b(boundary) - end_boundary = sep_boundary + b('--') - body = [] - for key, values in six.iteritems(data): - title = '\nContent-Disposition: form-data; name="%s"' % key - # handle multiple entries for the same name - if not isinstance(values, list): - values = [values] - for value in values: - if type(value) is tuple: - title += '; filename="%s"' % value[0] - value = value[1] - else: - value = b(value) - body.append(sep_boundary) - body.append(b(title)) - body.append(b("\n\n")) - body.append(value) - if value and value[-1:] == b('\r'): - body.append(b('\n')) # write an extra newline (lurve Macs) - body.append(end_boundary) - body.append(b("\n")) - body = b('').join(body) + body, ct = self._build_multipart(data) self.announce("Submitting documentation to %s" % (self.repository), log.INFO) @@ -164,7 +174,7 @@ class upload_docs(upload): try: conn.connect() conn.putrequest("POST", url) - content_type = 'multipart/form-data; boundary=%s' % boundary + content_type = ct conn.putheader('Content-type', content_type) conn.putheader('Content-length', str(len(body))) conn.putheader('Authorization', auth) diff --git a/setuptools/package_index.py b/setuptools/package_index.py index c53343e4..e87504db 100755 --- a/setuptools/package_index.py +++ b/setuptools/package_index.py @@ -17,6 +17,7 @@ except ImportError: from setuptools.extern import six from setuptools.extern.six.moves import urllib, http_client, configparser, map +import setuptools from pkg_resources import ( CHECKOUT_DIST, Distribution, BINARY_DIST, normalize_path, SOURCE_DIST, require, Environment, find_distributions, safe_name, safe_version, @@ -46,6 +47,11 @@ __all__ = [ _SOCKET_TIMEOUT = 15 + +_tmpl = "setuptools/{setuptools.__version__} Python-urllib/{py_major}" +user_agent = _tmpl.format(py_major=sys.version[:3], **globals()) + + def parse_bdist_wininst(name): """Return (base,pyversion) or (None,None) for possible .exe name""" @@ -202,9 +208,6 @@ def find_external_links(url, page): if match: yield urllib.parse.urljoin(url, htmldecode(match.group(1))) -user_agent = "Python-urllib/%s setuptools/%s" % ( - sys.version[:3], require('setuptools')[0].version -) class ContentChecker(object): """ diff --git a/setuptools/tests/test_upload_docs.py b/setuptools/tests/test_upload_docs.py index cc71cadb..d3dee616 100644 --- a/setuptools/tests/test_upload_docs.py +++ b/setuptools/tests/test_upload_docs.py @@ -57,3 +57,15 @@ class TestUploadDocsTest: with contextlib.closing(zipfile.ZipFile(tmp_file)) as zip_file: assert zip_file.namelist() == ['index.html'] + + def test_build_multipart(self): + data = dict( + a="foo", + b="bar", + file=('file.txt', b'content'), + ) + body, content_type = upload_docs._build_multipart(data) + assert 'form-data' in content_type + assert isinstance(body, bytes) + assert b'foo' in body + assert b'content' in body |
