summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.hgtags2
-rw-r--r--CHANGES.rst18
-rwxr-xr-xsetup.cfg2
-rwxr-xr-xsetup.py2
-rw-r--r--setuptools/command/test.py21
-rw-r--r--setuptools/command/upload_docs.py88
-rwxr-xr-xsetuptools/package_index.py9
-rw-r--r--setuptools/tests/test_upload_docs.py12
8 files changed, 104 insertions, 50 deletions
diff --git a/.hgtags b/.hgtags
index 400e6e89..d89ecbb2 100644
--- a/.hgtags
+++ b/.hgtags
@@ -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.
diff --git a/setup.cfg b/setup.cfg
index e9f13074..e0943717 100755
--- a/setup.cfg
+++ b/setup.cfg
@@ -1,5 +1,5 @@
[bumpversion]
-current_version = 21.2.1
+current_version = 22.0.0
commit = True
tag = True
diff --git a/setup.py b/setup.py
index 4ee0ac32..cc490950 100755
--- a/setup.py
+++ b/setup.py
@@ -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