From c1112aa5fb7da9996e5a0ebe8689e5cf9ca72cee Mon Sep 17 00:00:00 2001 From: PJ Eby Date: Thu, 7 Jul 2005 16:28:43 +0000 Subject: Add upload support to setuptools, and make default downloads of setuptools come from PyPI/python.org rather than from telecommunity.com. Bump to version 0.5a7. --HG-- branch : setuptools extra : convert_revision : svn%3A6015fed2-1504-0410-9fe1-9d1591cc4771/sandbox/trunk/setuptools%4041090 --- setuptools/command/upload.py | 171 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 171 insertions(+) create mode 100755 setuptools/command/upload.py (limited to 'setuptools/command/upload.py') diff --git a/setuptools/command/upload.py b/setuptools/command/upload.py new file mode 100755 index 00000000..bad01577 --- /dev/null +++ b/setuptools/command/upload.py @@ -0,0 +1,171 @@ +"""distutils.command.upload + +Implements the Distutils 'upload' subcommand (upload package to PyPI).""" + +from distutils.errors import * +from distutils.core import Command +from distutils.spawn import spawn +from distutils import log +from md5 import md5 +import os +import socket +import platform +import ConfigParser +import httplib +import base64 +import urlparse +import cStringIO as StringIO + +class upload(Command): + + description = "upload binary package to PyPI" + + DEFAULT_REPOSITORY = 'http://www.python.org/pypi' + + user_options = [ + ('repository=', 'r', + "url of repository [default: %s]" % DEFAULT_REPOSITORY), + ('show-response', None, + 'display full response text from server'), + ('sign', 's', + 'sign files to upload using gpg'), + ] + boolean_options = ['show-response', 'sign'] + + def initialize_options(self): + self.username = '' + self.password = '' + self.repository = '' + self.show_response = 0 + self.sign = False + + def finalize_options(self): + if os.environ.has_key('HOME'): + rc = os.path.join(os.environ['HOME'], '.pypirc') + if os.path.exists(rc): + self.announce('Using PyPI login from %s' % rc) + config = ConfigParser.ConfigParser({ + 'username':'', + 'password':'', + 'repository':''}) + config.read(rc) + if not self.repository: + self.repository = config.get('server-login', 'repository') + if not self.username: + self.username = config.get('server-login', 'username') + if not self.password: + self.password = config.get('server-login', 'password') + if not self.repository: + self.repository = self.DEFAULT_REPOSITORY + + def run(self): + if not self.distribution.dist_files: + raise DistutilsOptionError("No dist file created in earlier command") + for command, pyversion, filename in self.distribution.dist_files: + self.upload_file(command, pyversion, filename) + + def upload_file(self, command, pyversion, filename): + # Sign if requested + if self.sign: + spawn(("gpg", "--detach-sign", "-a", filename), + dry_run=self.dry_run) + + # Fill in the data + content = open(filename,'rb').read() + basename = os.path.basename(filename) + if basename.endswith('.egg'): + basename += '.zip' + if command=='bdist_egg': + command='sdist' + data = { + ':action':'file_upload', + 'protcol_version':'1', + 'name':self.distribution.get_name(), + 'version':self.distribution.get_version(), + 'content':(basename,content), + 'filetype':command, + 'pyversion':pyversion, + 'md5_digest':md5(content).hexdigest(), + } + comment = '' + if command == 'bdist_rpm': + dist, version, id = platform.dist() + if dist: + comment = 'built for %s %s' % (dist, version) + elif command == 'bdist_dumb': + comment = 'built for %s' % platform.platform(terse=1) + data['comment'] = comment + + if self.sign: + data['gpg_signature'] = (os.path.basename(filename) + ".asc", + open(filename+".asc").read()) + + # set up the authentication + auth = "Basic " + base64.encodestring(self.username + ":" + self.password).strip() + + # Build up the MIME payload for the POST data + boundary = '--------------GHSKFJDLGDS7543FJKLFHRE75642756743254' + sep_boundary = '\n--' + boundary + end_boundary = sep_boundary + '--' + body = StringIO.StringIO() + for key, value in data.items(): + # handle multiple entries for the same name + if type(value) != type([]): + value = [value] + for value in value: + if type(value) is tuple: + fn = ';filename="%s"' % value[0] + value = value[1] + else: + fn = "" + value = str(value) + body.write(sep_boundary) + body.write('\nContent-Disposition: form-data; name="%s"'%key) + body.write(fn) + body.write("\n\n") + body.write(value) + if value and value[-1] == '\r': + body.write('\n') # write an extra newline (lurve Macs) + body.write(end_boundary) + body.write("\n") + body = body.getvalue() + + self.announce("Submitting %s to %s" % (filename, self.repository), log.INFO) + + # build the Request + # We can't use urllib2 since we need to send the Basic + # auth right with the first request + schema, netloc, url, params, query, fragments = \ + urlparse.urlparse(self.repository) + assert not params and not query and not fragments + if schema == 'http': + http = httplib.HTTPConnection(netloc) + elif schema == 'https': + http = httplib.HTTPSConnection(netloc) + else: + raise AssertionError, "unsupported schema "+schema + + data = '' + loglevel = log.INFO + try: + http.connect() + http.putrequest("POST", url) + http.putheader('Content-type', + 'multipart/form-data; boundary=%s'%boundary) + http.putheader('Content-length', str(len(body))) + http.putheader('Authorization', auth) + http.endheaders() + http.send(body) + except socket.error, e: + self.announce(e.msg, log.ERROR) + return + + r = http.getresponse() + if r.status == 200: + self.announce('Server response (%s): %s' % (r.status, r.reason), + log.INFO) + else: + self.announce('Upload failed (%s): %s' % (r.status, r.reason), + log.ERROR) + if self.show_response: + print '-'*75, r.read(), '-'*75 -- cgit v1.2.1 From d3782636f9a68beba1dbefe96421d65af215e48a Mon Sep 17 00:00:00 2001 From: PJ Eby Date: Sat, 9 Jul 2005 04:26:21 +0000 Subject: Add informative comment when uploading eggs, to help distinguish them from source archives. --HG-- branch : setuptools extra : convert_revision : svn%3A6015fed2-1504-0410-9fe1-9d1591cc4771/sandbox/trunk/setuptools%4041104 --- setuptools/command/upload.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'setuptools/command/upload.py') diff --git a/setuptools/command/upload.py b/setuptools/command/upload.py index bad01577..4aa73cd1 100755 --- a/setuptools/command/upload.py +++ b/setuptools/command/upload.py @@ -75,8 +75,10 @@ class upload(Command): basename = os.path.basename(filename) if basename.endswith('.egg'): basename += '.zip' + comment = '' if command=='bdist_egg': command='sdist' + comment='Binary egg for use with setuptools' data = { ':action':'file_upload', 'protcol_version':'1', @@ -87,7 +89,6 @@ class upload(Command): 'pyversion':pyversion, 'md5_digest':md5(content).hexdigest(), } - comment = '' if command == 'bdist_rpm': dist, version, id = platform.dist() if dist: -- cgit v1.2.1 From 46eb9e6507ed12b79ec70b55ac2ffb74543bccd1 Mon Sep 17 00:00:00 2001 From: PJ Eby Date: Sun, 21 Aug 2005 22:12:12 +0000 Subject: Thanks to Richard Jones, we no longer need to fake out PyPI with a '.zip' extension for eggs. --HG-- branch : setuptools extra : convert_revision : svn%3A6015fed2-1504-0410-9fe1-9d1591cc4771/sandbox/trunk/setuptools%4041213 --- setuptools/command/upload.py | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) (limited to 'setuptools/command/upload.py') diff --git a/setuptools/command/upload.py b/setuptools/command/upload.py index 4aa73cd1..94190c2a 100755 --- a/setuptools/command/upload.py +++ b/setuptools/command/upload.py @@ -73,12 +73,9 @@ class upload(Command): # Fill in the data content = open(filename,'rb').read() basename = os.path.basename(filename) - if basename.endswith('.egg'): - basename += '.zip' comment = '' - if command=='bdist_egg': - command='sdist' - comment='Binary egg for use with setuptools' + if command=='bdist_egg' and self.distribution.has_ext_modules(): + comment = "built on %s" % platform.platform(terse=1) data = { ':action':'file_upload', 'protcol_version':'1', -- cgit v1.2.1 From 529b06d3717fdeb6a2070fa69b5c13cf0dff679f Mon Sep 17 00:00:00 2001 From: PJ Eby Date: Wed, 29 Mar 2006 21:45:01 +0000 Subject: Added ``--identity`` option to ``upload`` command. --HG-- branch : setuptools extra : convert_revision : svn%3A6015fed2-1504-0410-9fe1-9d1591cc4771/sandbox/trunk/setuptools%4043427 --- setuptools/command/upload.py | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) (limited to 'setuptools/command/upload.py') diff --git a/setuptools/command/upload.py b/setuptools/command/upload.py index 94190c2a..23cd0de9 100755 --- a/setuptools/command/upload.py +++ b/setuptools/command/upload.py @@ -29,6 +29,7 @@ class upload(Command): 'display full response text from server'), ('sign', 's', 'sign files to upload using gpg'), + ('identity=', 'i', 'GPG identity used to sign files'), ] boolean_options = ['show-response', 'sign'] @@ -38,8 +39,13 @@ class upload(Command): self.repository = '' self.show_response = 0 self.sign = False + self.identity = None def finalize_options(self): + if self.identity and not self.sign: + raise DistutilsOptionError( + "Must use --sign for --identity to have meaning" + ) if os.environ.has_key('HOME'): rc = os.path.join(os.environ['HOME'], '.pypirc') if os.path.exists(rc): @@ -67,7 +73,10 @@ class upload(Command): def upload_file(self, command, pyversion, filename): # Sign if requested if self.sign: - spawn(("gpg", "--detach-sign", "-a", filename), + gpg_args = ["gpg", "--detach-sign", "-a", filename] + if self.identity: + gpg_args[2:2] = ["--local-user", self.identity)] + spawn(gpg_args, dry_run=self.dry_run) # Fill in the data -- cgit v1.2.1 From f53d6de20758f122f293b099745bb381588cbc5f Mon Sep 17 00:00:00 2001 From: PJ Eby Date: Wed, 29 Mar 2006 23:40:35 +0000 Subject: Fix typo --HG-- branch : setuptools extra : convert_revision : svn%3A6015fed2-1504-0410-9fe1-9d1591cc4771/sandbox/trunk/setuptools%4043431 --- setuptools/command/upload.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'setuptools/command/upload.py') diff --git a/setuptools/command/upload.py b/setuptools/command/upload.py index 23cd0de9..644c400a 100755 --- a/setuptools/command/upload.py +++ b/setuptools/command/upload.py @@ -75,7 +75,7 @@ class upload(Command): if self.sign: gpg_args = ["gpg", "--detach-sign", "-a", filename] if self.identity: - gpg_args[2:2] = ["--local-user", self.identity)] + gpg_args[2:2] = ["--local-user", self.identity] spawn(gpg_args, dry_run=self.dry_run) -- cgit v1.2.1 From dc749c307648324f56cd42b30173d21dde79c1b2 Mon Sep 17 00:00:00 2001 From: PJ Eby Date: Mon, 10 Jul 2006 19:51:20 +0000 Subject: Fix broken error message for socket error during upload. (backport from trunk) --HG-- branch : setuptools-0.6 extra : convert_revision : svn%3A6015fed2-1504-0410-9fe1-9d1591cc4771/sandbox/branches/setuptools-0.6%4050530 --- setuptools/command/upload.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'setuptools/command/upload.py') diff --git a/setuptools/command/upload.py b/setuptools/command/upload.py index 644c400a..685ce43b 100755 --- a/setuptools/command/upload.py +++ b/setuptools/command/upload.py @@ -164,7 +164,7 @@ class upload(Command): http.endheaders() http.send(body) except socket.error, e: - self.announce(e.msg, log.ERROR) + self.announce(str(e), log.ERROR) return r = http.getresponse() -- cgit v1.2.1 From a44ba4da6f1bbdc0290905c139becf9b728cb0f6 Mon Sep 17 00:00:00 2001 From: PJ Eby Date: Tue, 4 Sep 2007 15:56:34 +0000 Subject: The default ``--index-url`` is now ``http://pypi.python.org/simple``, to use the Python Package Index's new simpler (and faster!) REST API. (backport from trunk) --HG-- branch : setuptools-0.6 extra : convert_revision : svn%3A6015fed2-1504-0410-9fe1-9d1591cc4771/sandbox/branches/setuptools-0.6%4057965 --- setuptools/command/upload.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'setuptools/command/upload.py') diff --git a/setuptools/command/upload.py b/setuptools/command/upload.py index 685ce43b..e91b8540 100755 --- a/setuptools/command/upload.py +++ b/setuptools/command/upload.py @@ -20,7 +20,7 @@ class upload(Command): description = "upload binary package to PyPI" - DEFAULT_REPOSITORY = 'http://www.python.org/pypi' + DEFAULT_REPOSITORY = 'http://pypi.python.org/pypi' user_options = [ ('repository=', 'r', -- cgit v1.2.1 From 2d3007539b9ed75f8f5d5a35284640a03235f422 Mon Sep 17 00:00:00 2001 From: PJ Eby Date: Thu, 21 Aug 2008 18:00:54 +0000 Subject: Fix for http://bugs.python.org/setuptools/issue5 (backport from trunk) --HG-- branch : setuptools-0.6 extra : convert_revision : svn%3A6015fed2-1504-0410-9fe1-9d1591cc4771/sandbox/branches/setuptools-0.6%4065945 --- setuptools/command/upload.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'setuptools/command/upload.py') diff --git a/setuptools/command/upload.py b/setuptools/command/upload.py index e91b8540..7ac08c22 100755 --- a/setuptools/command/upload.py +++ b/setuptools/command/upload.py @@ -6,7 +6,10 @@ from distutils.errors import * from distutils.core import Command from distutils.spawn import spawn from distutils import log -from md5 import md5 +try: + from hashlib import md5 +except ImportError: + from md5 import md5 import os import socket import platform -- cgit v1.2.1 From 334f5b69c08cc7b3f60f963a689987412db2e635 Mon Sep 17 00:00:00 2001 From: Hanno Schlichting Date: Thu, 16 Jul 2009 16:57:09 +0200 Subject: Apply patch from pjenvey. Closes #3. --HG-- branch : distribute extra : rebase_source : 3a61d0692c74559b140c179dcc5f4ac4905bb982 --- setuptools/command/upload.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'setuptools/command/upload.py') diff --git a/setuptools/command/upload.py b/setuptools/command/upload.py index 7ac08c22..1f49745e 100755 --- a/setuptools/command/upload.py +++ b/setuptools/command/upload.py @@ -83,7 +83,9 @@ class upload(Command): dry_run=self.dry_run) # Fill in the data - content = open(filename,'rb').read() + f = open(filename,'rb') + content = f.read() + f.close() basename = os.path.basename(filename) comment = '' if command=='bdist_egg' and self.distribution.has_ext_modules(): -- cgit v1.2.1 From 58a658b26d1c95b31d02050dcccd648d2e4ce27b Mon Sep 17 00:00:00 2001 From: Vinay Sajip Date: Mon, 20 Jun 2011 22:55:16 +0100 Subject: Changes to support 2.x and 3.x in the same codebase. --HG-- branch : distribute extra : rebase_source : 7d3608edee54a43789f0574d702fb839628b5071 --- setuptools/command/upload.py | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) (limited to 'setuptools/command/upload.py') diff --git a/setuptools/command/upload.py b/setuptools/command/upload.py index 1f49745e..6b18d761 100755 --- a/setuptools/command/upload.py +++ b/setuptools/command/upload.py @@ -11,13 +11,12 @@ try: except ImportError: from md5 import md5 import os +import sys import socket import platform -import ConfigParser -import httplib import base64 -import urlparse -import cStringIO as StringIO + +from setuptools.compat import urlparse, StringIO, httplib, ConfigParser class upload(Command): @@ -49,7 +48,7 @@ class upload(Command): raise DistutilsOptionError( "Must use --sign for --identity to have meaning" ) - if os.environ.has_key('HOME'): + if 'HOME' in os.environ: rc = os.path.join(os.environ['HOME'], '.pypirc') if os.path.exists(rc): self.announce('Using PyPI login from %s' % rc) @@ -148,14 +147,14 @@ class upload(Command): # We can't use urllib2 since we need to send the Basic # auth right with the first request schema, netloc, url, params, query, fragments = \ - urlparse.urlparse(self.repository) + urlparse(self.repository) assert not params and not query and not fragments if schema == 'http': http = httplib.HTTPConnection(netloc) elif schema == 'https': http = httplib.HTTPSConnection(netloc) else: - raise AssertionError, "unsupported schema "+schema + raise AssertionError("unsupported schema " + schema) data = '' loglevel = log.INFO @@ -168,7 +167,8 @@ class upload(Command): http.putheader('Authorization', auth) http.endheaders() http.send(body) - except socket.error, e: + except socket.error: + e = sys.exc_info()[1] self.announce(str(e), log.ERROR) return @@ -180,4 +180,4 @@ class upload(Command): self.announce('Upload failed (%s): %s' % (r.status, r.reason), log.ERROR) if self.show_response: - print '-'*75, r.read(), '-'*75 + print('-'*75, r.read(), '-'*75) -- cgit v1.2.1 From 4f5c1303ca73fdb1860bf00ee9a37a2c1b6fe294 Mon Sep 17 00:00:00 2001 From: guyroz Date: Tue, 20 Sep 2011 16:40:58 +0300 Subject: Issue #244 raises ValueError in upload and register commands if using a section without a repository value --HG-- branch : distribute extra : rebase_source : e57437a8ac03832ed8170c902996423a27235856 --- setuptools/command/upload.py | 78 ++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 76 insertions(+), 2 deletions(-) (limited to 'setuptools/command/upload.py') diff --git a/setuptools/command/upload.py b/setuptools/command/upload.py index 1f49745e..042fcbb5 100755 --- a/setuptools/command/upload.py +++ b/setuptools/command/upload.py @@ -2,6 +2,7 @@ Implements the Distutils 'upload' subcommand (upload package to PyPI).""" + from distutils.errors import * from distutils.core import Command from distutils.spawn import spawn @@ -11,6 +12,7 @@ try: except ImportError: from md5 import md5 import os +import sys import socket import platform import ConfigParser @@ -19,7 +21,7 @@ import base64 import urlparse import cStringIO as StringIO -class upload(Command): +class _upload(Command): description = "upload binary package to PyPI" @@ -65,7 +67,7 @@ class upload(Command): if not self.password: self.password = config.get('server-login', 'password') if not self.repository: - self.repository = self.DEFAULT_REPOSITORY + raise ValueError('%s is missing a repository value in .pypirc' % self._section_name) def run(self): if not self.distribution.dist_files: @@ -181,3 +183,75 @@ class upload(Command): log.ERROR) if self.show_response: print '-'*75, r.read(), '-'*75 + +if sys.version >= "2.5": + from distutils.command.upload import upload as distutils_upload + class upload(distutils_upload): + + def run(self): + self._section_name = self.repository + distutils_upload.run(self) + + def _read_pypirc(self): + """Reads the .pypirc file.""" + self._section_name = self.repository + rc = self._get_rc_file() + if os.path.exists(rc): + repository = self.repository + config = ConfigParser.ConfigParser() + config.read(rc) + sections = config.sections() + if 'distutils' in sections: + # let's get the list of servers + index_servers = config.get('distutils', 'index-servers') + _servers = [server.strip() for server in + index_servers.split('\n') + if server.strip() != ''] + if _servers == []: + # nothing set, let's try to get the default pypi + if 'pypi' in sections: + _servers = ['pypi'] + else: + # the file is not properly defined, returning + # an empty dict + return {} + for server in _servers: + current = {'server': server} + current['username'] = config.get(server, 'username') + + # optional params + for key, default in (('repository', + None), + ('realm', self.DEFAULT_REALM), + ('password', None)): + if config.has_option(server, key): + current[key] = config.get(server, key) + else: + current[key] = default + if (current['server'] == repository or + current['repository'] == repository): + return current + elif 'server-login' in sections: + # old format + server = 'server-login' + if config.has_option(server, 'repository'): + repository = config.get(server, 'repository') + else: + repository = None + return {'username': config.get(server, 'username'), + 'password': config.get(server, 'password'), + 'repository': repository, + 'server': server, + 'realm': self.DEFAULT_REALM} + + return {} + + def finalize_options(self): + distutils_upload.finalize_options(self) + if not self.repository: + raise ValueError('%s is missing a repository value in .pypirc' % self._section_name) + + +else: + upload = _upload + -- cgit v1.2.1 From d36c067b3247bf7dc3102051b088fe744ac74ae0 Mon Sep 17 00:00:00 2001 From: guyroz Date: Thu, 22 Sep 2011 20:50:45 +0300 Subject: Issue #246 --HG-- branch : distribute extra : rebase_source : e14172505ff8938f00f51de4f29a8fb2834ac933 --- setuptools/command/upload.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'setuptools/command/upload.py') diff --git a/setuptools/command/upload.py b/setuptools/command/upload.py index 042fcbb5..1b708943 100755 --- a/setuptools/command/upload.py +++ b/setuptools/command/upload.py @@ -228,8 +228,10 @@ if sys.version >= "2.5": current[key] = config.get(server, key) else: current[key] = default + # Issue #246, handling url ambiguity if (current['server'] == repository or - current['repository'] == repository): + current['repository'] == repository or + (current['repository'] == "http://www.python.org/pypi" and repository == self.DEFAULT_REPOSITORY)): return current elif 'server-login' in sections: # old format -- cgit v1.2.1 From 257f23372a797ede9e14ae3e62c8319896943857 Mon Sep 17 00:00:00 2001 From: "Guy Rozendorn (guyr@infinidat.com)" Date: Fri, 7 Oct 2011 18:55:31 +0200 Subject: Revert 8d1cb51a01b6 because of issue #250 --HG-- branch : distribute extra : rebase_source : 842a0d796e6e3cd5ecf312dd5c3ff2bb35d601a5 --- setuptools/command/upload.py | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) (limited to 'setuptools/command/upload.py') diff --git a/setuptools/command/upload.py b/setuptools/command/upload.py index 1b708943..f2e0b2e1 100755 --- a/setuptools/command/upload.py +++ b/setuptools/command/upload.py @@ -228,10 +228,7 @@ if sys.version >= "2.5": current[key] = config.get(server, key) else: current[key] = default - # Issue #246, handling url ambiguity - if (current['server'] == repository or - current['repository'] == repository or - (current['repository'] == "http://www.python.org/pypi" and repository == self.DEFAULT_REPOSITORY)): + if (current['server'] == repository: return current elif 'server-login' in sections: # old format -- cgit v1.2.1 From d5df4531433b2031186ed2a8b335892b3600912b Mon Sep 17 00:00:00 2001 From: "Guy Rozendorn (guyr@infinidat.com)" Date: Fri, 7 Oct 2011 19:08:55 +0200 Subject: Reverting 1a1ab844f03e due to issue 250 --HG-- branch : distribute extra : rebase_source : 0dca5e604c96429f07c4d7786d3a0991c42c129d --- setuptools/command/upload.py | 76 ++------------------------------------------ 1 file changed, 2 insertions(+), 74 deletions(-) (limited to 'setuptools/command/upload.py') diff --git a/setuptools/command/upload.py b/setuptools/command/upload.py index f2e0b2e1..4bd6021d 100755 --- a/setuptools/command/upload.py +++ b/setuptools/command/upload.py @@ -2,7 +2,6 @@ Implements the Distutils 'upload' subcommand (upload package to PyPI).""" - from distutils.errors import * from distutils.core import Command from distutils.spawn import spawn @@ -12,7 +11,6 @@ try: except ImportError: from md5 import md5 import os -import sys import socket import platform import ConfigParser @@ -21,7 +19,7 @@ import base64 import urlparse import cStringIO as StringIO -class _upload(Command): +class upload(Command): description = "upload binary package to PyPI" @@ -67,7 +65,7 @@ class _upload(Command): if not self.password: self.password = config.get('server-login', 'password') if not self.repository: - raise ValueError('%s is missing a repository value in .pypirc' % self._section_name) + self.repository = self.DEFAULT_REPOSITORY def run(self): if not self.distribution.dist_files: @@ -184,73 +182,3 @@ class _upload(Command): if self.show_response: print '-'*75, r.read(), '-'*75 -if sys.version >= "2.5": - from distutils.command.upload import upload as distutils_upload - class upload(distutils_upload): - - def run(self): - self._section_name = self.repository - distutils_upload.run(self) - - def _read_pypirc(self): - """Reads the .pypirc file.""" - self._section_name = self.repository - rc = self._get_rc_file() - if os.path.exists(rc): - repository = self.repository - config = ConfigParser.ConfigParser() - config.read(rc) - sections = config.sections() - if 'distutils' in sections: - # let's get the list of servers - index_servers = config.get('distutils', 'index-servers') - _servers = [server.strip() for server in - index_servers.split('\n') - if server.strip() != ''] - if _servers == []: - # nothing set, let's try to get the default pypi - if 'pypi' in sections: - _servers = ['pypi'] - else: - # the file is not properly defined, returning - # an empty dict - return {} - for server in _servers: - current = {'server': server} - current['username'] = config.get(server, 'username') - - # optional params - for key, default in (('repository', - None), - ('realm', self.DEFAULT_REALM), - ('password', None)): - if config.has_option(server, key): - current[key] = config.get(server, key) - else: - current[key] = default - if (current['server'] == repository: - return current - elif 'server-login' in sections: - # old format - server = 'server-login' - if config.has_option(server, 'repository'): - repository = config.get(server, 'repository') - else: - repository = None - return {'username': config.get(server, 'username'), - 'password': config.get(server, 'password'), - 'repository': repository, - 'server': server, - 'realm': self.DEFAULT_REALM} - - return {} - - def finalize_options(self): - distutils_upload.finalize_options(self) - if not self.repository: - raise ValueError('%s is missing a repository value in .pypirc' % self._section_name) - - -else: - upload = _upload - -- cgit v1.2.1 From 8b6d10f3443e8f9b8d662097d496392a4e7ed599 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Mon, 16 Apr 2012 15:30:31 -0400 Subject: Fix typo in protocol_version. Thanks aclark! --HG-- branch : distribute extra : rebase_source : ab90cfb5cae3189b8d0c71c43992bc0273a7587a --- setuptools/command/upload.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'setuptools/command/upload.py') diff --git a/setuptools/command/upload.py b/setuptools/command/upload.py index 4bd6021d..9f9366b5 100755 --- a/setuptools/command/upload.py +++ b/setuptools/command/upload.py @@ -92,7 +92,7 @@ class upload(Command): comment = "built on %s" % platform.platform(terse=1) data = { ':action':'file_upload', - 'protcol_version':'1', + 'protocol_version':'1', 'name':self.distribution.get_name(), 'version':self.distribution.get_version(), 'content':(basename,content), -- cgit v1.2.1 From 721559fe76a1cda3de639ee2aaa446595169d26b Mon Sep 17 00:00:00 2001 From: Arfrever Frehtes Taifersar Arahesis Date: Sat, 29 Dec 2012 06:23:09 +0100 Subject: Fix some ResourceWarnings. --HG-- branch : distribute extra : rebase_source : 31ac3f0135d8cfe0fabc274f1649d1c99eba2868 --- setuptools/command/upload.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'setuptools/command/upload.py') diff --git a/setuptools/command/upload.py b/setuptools/command/upload.py index 9f9366b5..21b9615c 100755 --- a/setuptools/command/upload.py +++ b/setuptools/command/upload.py @@ -109,8 +109,9 @@ class upload(Command): data['comment'] = comment if self.sign: - data['gpg_signature'] = (os.path.basename(filename) + ".asc", - open(filename+".asc").read()) + asc_file = open(filename + ".asc") + data['gpg_signature'] = (os.path.basename(filename) + ".asc", asc_file.read()) + asc_file.close() # set up the authentication auth = "Basic " + base64.encodestring(self.username + ":" + self.password).strip() -- cgit v1.2.1 From fbbc3e1980d80e113852951f36aaaccc72c57c9e Mon Sep 17 00:00:00 2001 From: Arfrever Frehtes Taifersar Arahesis Date: Tue, 11 Jun 2013 08:50:52 +0200 Subject: Update some URLs. --- setuptools/command/upload.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'setuptools/command/upload.py') diff --git a/setuptools/command/upload.py b/setuptools/command/upload.py index 21b9615c..4b500f68 100755 --- a/setuptools/command/upload.py +++ b/setuptools/command/upload.py @@ -23,7 +23,7 @@ class upload(Command): description = "upload binary package to PyPI" - DEFAULT_REPOSITORY = 'http://pypi.python.org/pypi' + DEFAULT_REPOSITORY = 'https://pypi.python.org/pypi' user_options = [ ('repository=', 'r', -- cgit v1.2.1 From bd959a59df72cacea485bedccd8f5d452e0aa353 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Tue, 3 Sep 2013 22:39:26 -0400 Subject: Remove import * --- setuptools/command/upload.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'setuptools/command/upload.py') diff --git a/setuptools/command/upload.py b/setuptools/command/upload.py index 02d955ed..8d258d5a 100755 --- a/setuptools/command/upload.py +++ b/setuptools/command/upload.py @@ -2,10 +2,10 @@ Implements the Distutils 'upload' subcommand (upload package to PyPI).""" -from distutils.errors import * +from distutils import errors +from distutils import log from distutils.core import Command from distutils.spawn import spawn -from distutils import log try: from hashlib import md5 except ImportError: @@ -45,7 +45,7 @@ class upload(Command): def finalize_options(self): if self.identity and not self.sign: - raise DistutilsOptionError( + raise errors.DistutilsOptionError( "Must use --sign for --identity to have meaning" ) if 'HOME' in os.environ: @@ -68,7 +68,7 @@ class upload(Command): def run(self): if not self.distribution.dist_files: - raise DistutilsOptionError("No dist file created in earlier command") + raise errors.DistutilsOptionError("No dist file created in earlier command") for command, pyversion, filename in self.distribution.dist_files: self.upload_file(command, pyversion, filename) -- cgit v1.2.1 From 701115d88ba8b59b890672d131bc061c80e889c9 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Tue, 3 Sep 2013 22:40:40 -0400 Subject: Use isinstance for type comparison --- setuptools/command/upload.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'setuptools/command/upload.py') diff --git a/setuptools/command/upload.py b/setuptools/command/upload.py index 8d258d5a..93da7a39 100755 --- a/setuptools/command/upload.py +++ b/setuptools/command/upload.py @@ -122,7 +122,7 @@ class upload(Command): body = StringIO.StringIO() for key, value in data.items(): # handle multiple entries for the same name - if type(value) != type([]): + if isinstance(value, list): value = [value] for value in value: if type(value) is tuple: -- cgit v1.2.1 From aa3e70d8a887dc71ecc8170d04a623fa5542360f Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Tue, 3 Sep 2013 22:41:05 -0400 Subject: Remove unused variable --- setuptools/command/upload.py | 1 - 1 file changed, 1 deletion(-) (limited to 'setuptools/command/upload.py') diff --git a/setuptools/command/upload.py b/setuptools/command/upload.py index 93da7a39..dde4decd 100755 --- a/setuptools/command/upload.py +++ b/setuptools/command/upload.py @@ -158,7 +158,6 @@ class upload(Command): raise AssertionError("unsupported schema " + schema) data = '' - loglevel = log.INFO try: http.connect() http.putrequest("POST", url) -- cgit v1.2.1 From ca3e8a370652c0735cb3921d43152e1014bbd946 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Tue, 3 Sep 2013 22:46:55 -0400 Subject: Correct AttributeError in upload command on Python 2.4. Fixes #76 --- setuptools/command/upload.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'setuptools/command/upload.py') diff --git a/setuptools/command/upload.py b/setuptools/command/upload.py index dde4decd..5476b5ec 100755 --- a/setuptools/command/upload.py +++ b/setuptools/command/upload.py @@ -119,7 +119,7 @@ class upload(Command): boundary = '--------------GHSKFJDLGDS7543FJKLFHRE75642756743254' sep_boundary = '\n--' + boundary end_boundary = sep_boundary + '--' - body = StringIO.StringIO() + body = StringIO() for key, value in data.items(): # handle multiple entries for the same name if isinstance(value, list): -- cgit v1.2.1 From 3117a6c743787dc2d409d4c34934d3bcc8f27206 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sat, 7 Sep 2013 17:10:09 -0400 Subject: Fix boolean test, incorrectly changed. Off-by-one errors are particularly ugly with booleans ;) Fixes #77 --- setuptools/command/upload.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'setuptools/command/upload.py') diff --git a/setuptools/command/upload.py b/setuptools/command/upload.py index 93da7a39..575e121e 100755 --- a/setuptools/command/upload.py +++ b/setuptools/command/upload.py @@ -122,7 +122,7 @@ class upload(Command): body = StringIO.StringIO() for key, value in data.items(): # handle multiple entries for the same name - if isinstance(value, list): + if not isinstance(value, list): value = [value] for value in value: if type(value) is tuple: -- cgit v1.2.1 From 39dd5934be0a44d1f0452975a17784bddb276a82 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sun, 24 Nov 2013 16:04:34 -0500 Subject: Remove upload command (no longer relevant on Python 2.6+ --- setuptools/command/upload.py | 183 ------------------------------------------- 1 file changed, 183 deletions(-) delete mode 100755 setuptools/command/upload.py (limited to 'setuptools/command/upload.py') diff --git a/setuptools/command/upload.py b/setuptools/command/upload.py deleted file mode 100755 index a6eff385..00000000 --- a/setuptools/command/upload.py +++ /dev/null @@ -1,183 +0,0 @@ -"""distutils.command.upload - -Implements the Distutils 'upload' subcommand (upload package to PyPI).""" - -from distutils import errors -from distutils import log -from distutils.core import Command -from distutils.spawn import spawn -try: - from hashlib import md5 -except ImportError: - from md5 import md5 -import os -import sys -import socket -import platform -import base64 - -from setuptools.compat import urlparse, StringIO, httplib, ConfigParser - -class upload(Command): - - description = "upload binary package to PyPI" - - DEFAULT_REPOSITORY = 'https://pypi.python.org/pypi' - - user_options = [ - ('repository=', 'r', - "url of repository [default: %s]" % DEFAULT_REPOSITORY), - ('show-response', None, - 'display full response text from server'), - ('sign', 's', - 'sign files to upload using gpg'), - ('identity=', 'i', 'GPG identity used to sign files'), - ] - boolean_options = ['show-response', 'sign'] - - def initialize_options(self): - self.username = '' - self.password = '' - self.repository = '' - self.show_response = 0 - self.sign = False - self.identity = None - - def finalize_options(self): - if self.identity and not self.sign: - raise errors.DistutilsOptionError( - "Must use --sign for --identity to have meaning" - ) - if 'HOME' in os.environ: - rc = os.path.join(os.environ['HOME'], '.pypirc') - if os.path.exists(rc): - self.announce('Using PyPI login from %s' % rc) - config = ConfigParser.ConfigParser({ - 'username':'', - 'password':'', - 'repository':''}) - config.read(rc) - if not self.repository: - self.repository = config.get('server-login', 'repository') - if not self.username: - self.username = config.get('server-login', 'username') - if not self.password: - self.password = config.get('server-login', 'password') - if not self.repository: - self.repository = self.DEFAULT_REPOSITORY - - def run(self): - if not self.distribution.dist_files: - raise errors.DistutilsOptionError("No dist file created in earlier command") - for command, pyversion, filename in self.distribution.dist_files: - self.upload_file(command, pyversion, filename) - - def upload_file(self, command, pyversion, filename): - # Sign if requested - if self.sign: - gpg_args = ["gpg", "--detach-sign", "-a", filename] - if self.identity: - gpg_args[2:2] = ["--local-user", self.identity] - spawn(gpg_args, - dry_run=self.dry_run) - - # Fill in the data - f = open(filename,'rb') - content = f.read() - f.close() - basename = os.path.basename(filename) - comment = '' - if command=='bdist_egg' and self.distribution.has_ext_modules(): - comment = "built on %s" % platform.platform(terse=1) - data = { - ':action':'file_upload', - 'protocol_version':'1', - 'name':self.distribution.get_name(), - 'version':self.distribution.get_version(), - 'content':(basename,content), - 'filetype':command, - 'pyversion':pyversion, - 'md5_digest':md5(content).hexdigest(), - } - if command == 'bdist_rpm': - dist, version, id = platform.dist() - if dist: - comment = 'built for %s %s' % (dist, version) - elif command == 'bdist_dumb': - comment = 'built for %s' % platform.platform(terse=1) - data['comment'] = comment - - if self.sign: - asc_file = open(filename + ".asc") - data['gpg_signature'] = (os.path.basename(filename) + ".asc", asc_file.read()) - asc_file.close() - - # set up the authentication - auth = "Basic " + base64.encodestring(self.username + ":" + self.password).strip() - - # Build up the MIME payload for the POST data - boundary = '--------------GHSKFJDLGDS7543FJKLFHRE75642756743254' - sep_boundary = '\n--' + boundary - end_boundary = sep_boundary + '--' - body = StringIO() - for key, value in data.items(): - # handle multiple entries for the same name - if not isinstance(value, list): - value = [value] - for value in value: - if type(value) is tuple: - fn = ';filename="%s"' % value[0] - value = value[1] - else: - fn = "" - value = str(value) - body.write(sep_boundary) - body.write('\nContent-Disposition: form-data; name="%s"'%key) - body.write(fn) - body.write("\n\n") - body.write(value) - if value and value[-1] == '\r': - body.write('\n') # write an extra newline (lurve Macs) - body.write(end_boundary) - body.write("\n") - body = body.getvalue() - - self.announce("Submitting %s to %s" % (filename, self.repository), log.INFO) - - # build the Request - # We can't use urllib2 since we need to send the Basic - # auth right with the first request - schema, netloc, url, params, query, fragments = \ - urlparse(self.repository) - assert not params and not query and not fragments - if schema == 'http': - http = httplib.HTTPConnection(netloc) - elif schema == 'https': - http = httplib.HTTPSConnection(netloc) - else: - raise AssertionError("unsupported schema " + schema) - - data = '' - try: - http.connect() - http.putrequest("POST", url) - http.putheader('Content-type', - 'multipart/form-data; boundary=%s'%boundary) - http.putheader('Content-length', str(len(body))) - http.putheader('Authorization', auth) - http.endheaders() - http.send(body) - except socket.error: - e = sys.exc_info()[1] - self.announce(str(e), log.ERROR) - return - - r = http.getresponse() - if r.status == 200: - self.announce('Server response (%s): %s' % (r.status, r.reason), - log.INFO) - else: - self.announce('Upload failed (%s): %s' % (r.status, r.reason), - log.ERROR) - if self.show_response: - print('-'*75, r.read(), '-'*75) -- cgit v1.2.1 From 5367a7399762a9098ea689c7cdcb54fb9748dd66 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Thu, 11 Feb 2016 23:47:28 -0500 Subject: Override upload command to load passwords from keyring when available and not otherwise specified. --- setuptools/command/upload.py | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) create mode 100644 setuptools/command/upload.py (limited to 'setuptools/command/upload.py') diff --git a/setuptools/command/upload.py b/setuptools/command/upload.py new file mode 100644 index 00000000..08c20ba8 --- /dev/null +++ b/setuptools/command/upload.py @@ -0,0 +1,23 @@ +from distutils.command import upload as orig + + +class upload(orig.upload): + """ + Override default upload behavior to look up password + in the keyring if available. + """ + + def finalize_options(self): + orig.upload.finalize_options(self) + self.password or self._load_password_from_keyring() + + def _load_password_from_keyring(self): + """ + Attempt to load password from keyring. Suppress Exceptions. + """ + try: + keyring = __import__('keyring') + self.password = keyring.get_password(self.repository, + self.username) + except Exception: + pass -- cgit v1.2.1 From 6c9c3c7e4d53394149c82637f18c0e55d4baca65 Mon Sep 17 00:00:00 2001 From: Brooks Kindle Date: Sat, 23 Apr 2016 11:01:39 -0700 Subject: Prompt for password on upload. The upload command wasn't prompting for a password. If there was no password specified in ~/.pypirc, the upload command, python setup.py sdist upload would fail and raise a TypeError. The workaround for this was to use python setup.py sdist register upload since the register command does prompt for a password. This commit ensures that the upload command prompts for a password if not given one by ~/.pypirc or the register command. --- setuptools/command/upload.py | 32 ++++++++++++++++++++++++++------ 1 file changed, 26 insertions(+), 6 deletions(-) (limited to 'setuptools/command/upload.py') diff --git a/setuptools/command/upload.py b/setuptools/command/upload.py index 08c20ba8..43c0b0d7 100644 --- a/setuptools/command/upload.py +++ b/setuptools/command/upload.py @@ -3,13 +3,18 @@ from distutils.command import upload as orig class upload(orig.upload): """ - Override default upload behavior to look up password - in the keyring if available. + Override default upload behavior to obtain password + in a variety of different ways. """ def finalize_options(self): orig.upload.finalize_options(self) - self.password or self._load_password_from_keyring() + # Attempt to obtain password. Short circuit evaluation at the first + # sign of success. + self.password = ( + self.password or self._load_password_from_keyring() or + self._prompt_for_password() + ) def _load_password_from_keyring(self): """ @@ -17,7 +22,22 @@ class upload(orig.upload): """ try: keyring = __import__('keyring') - self.password = keyring.get_password(self.repository, - self.username) + password = keyring.get_password(self.repository, self.username) except Exception: - pass + password = None + finally: + return password + + def _prompt_for_password(self): + """ + Prompt for a password on the tty. Suppress Exceptions. + """ + password = None + try: + import getpass + while not password: + password = getpass.getpass() + except (Exception, KeyboardInterrupt): + password = None + finally: + return password -- cgit v1.2.1 From 9b53df4becddd3efe5d8a2f15273ca6ce9fdb843 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sun, 24 Apr 2016 23:19:28 -0500 Subject: Move import to header --- setuptools/command/upload.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'setuptools/command/upload.py') diff --git a/setuptools/command/upload.py b/setuptools/command/upload.py index 43c0b0d7..0e8bbea3 100644 --- a/setuptools/command/upload.py +++ b/setuptools/command/upload.py @@ -1,3 +1,4 @@ +import getpass from distutils.command import upload as orig @@ -34,7 +35,6 @@ class upload(orig.upload): """ password = None try: - import getpass while not password: password = getpass.getpass() except (Exception, KeyboardInterrupt): -- cgit v1.2.1 From a3b119e14df4aff213231492470587f88457a241 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sun, 24 Apr 2016 23:20:51 -0500 Subject: Add carriage return for symmetry --- setuptools/command/upload.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'setuptools/command/upload.py') diff --git a/setuptools/command/upload.py b/setuptools/command/upload.py index 0e8bbea3..a001d461 100644 --- a/setuptools/command/upload.py +++ b/setuptools/command/upload.py @@ -13,7 +13,8 @@ class upload(orig.upload): # Attempt to obtain password. Short circuit evaluation at the first # sign of success. self.password = ( - self.password or self._load_password_from_keyring() or + self.password or + self._load_password_from_keyring() or self._prompt_for_password() ) -- cgit v1.2.1 From c105d6f18a5a17b0a47fda5a2df2f8f47352b037 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sun, 24 Apr 2016 23:25:33 -0500 Subject: Simplify logic by eliminating retries in password prompt and returning results directly. --- setuptools/command/upload.py | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) (limited to 'setuptools/command/upload.py') diff --git a/setuptools/command/upload.py b/setuptools/command/upload.py index a001d461..484baa5a 100644 --- a/setuptools/command/upload.py +++ b/setuptools/command/upload.py @@ -24,21 +24,15 @@ class upload(orig.upload): """ try: keyring = __import__('keyring') - password = keyring.get_password(self.repository, self.username) + return keyring.get_password(self.repository, self.username) except Exception: - password = None - finally: - return password + pass def _prompt_for_password(self): """ Prompt for a password on the tty. Suppress Exceptions. """ - password = None try: - while not password: - password = getpass.getpass() + return getpass.getpass() except (Exception, KeyboardInterrupt): - password = None - finally: - return password + pass -- cgit v1.2.1 From ed360af83a547d565aea8c20d3086486c4c9491c Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Thu, 2 Feb 2017 09:00:35 -0500 Subject: Infer the username using getpass.getuser() if no username is resolved from .pypirc. --- setuptools/command/upload.py | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'setuptools/command/upload.py') diff --git a/setuptools/command/upload.py b/setuptools/command/upload.py index 484baa5a..a44173a9 100644 --- a/setuptools/command/upload.py +++ b/setuptools/command/upload.py @@ -10,6 +10,10 @@ class upload(orig.upload): def finalize_options(self): orig.upload.finalize_options(self) + self.username = ( + self.username or + getpass.getuser() + ) # Attempt to obtain password. Short circuit evaluation at the first # sign of success. self.password = ( -- cgit v1.2.1 From a91c571942baf303ac8cbd2cc2676252bed7d4bc Mon Sep 17 00:00:00 2001 From: Dustin Ingram Date: Sun, 8 Jul 2018 13:26:08 -0500 Subject: Deprecate upload and register commands --- setuptools/command/upload.py | 11 +++++++++++ 1 file changed, 11 insertions(+) (limited to 'setuptools/command/upload.py') diff --git a/setuptools/command/upload.py b/setuptools/command/upload.py index a44173a9..72f24d8f 100644 --- a/setuptools/command/upload.py +++ b/setuptools/command/upload.py @@ -1,4 +1,5 @@ import getpass +from distutils import log from distutils.command import upload as orig @@ -8,6 +9,16 @@ class upload(orig.upload): in a variety of different ways. """ + def run(self): + try: + orig.upload.run(self) + finally: + self.announce( + "WARNING: Uploading via this command is deprecated, use twine " + "to upload instead (https://pypi.org/p/twine/)", + log.WARN + ) + def finalize_options(self): orig.upload.finalize_options(self) self.username = ( -- cgit v1.2.1 From cbef2949856f5e0957be1cbdfdc92325f9ae9f1e Mon Sep 17 00:00:00 2001 From: Paul Ganssle Date: Sat, 27 Oct 2018 17:21:11 -0400 Subject: Add upload_file to setuptools.command.upload --- setuptools/command/upload.py | 147 ++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 146 insertions(+), 1 deletion(-) (limited to 'setuptools/command/upload.py') diff --git a/setuptools/command/upload.py b/setuptools/command/upload.py index 72f24d8f..dae7d74d 100644 --- a/setuptools/command/upload.py +++ b/setuptools/command/upload.py @@ -1,14 +1,24 @@ +import io +import os +import hashlib import getpass + +from base64 import standard_b64encode + from distutils import log from distutils.command import upload as orig +from distutils.errors import DistutilsError + +from six.moves.urllib.request import urlopen, Request +from six.moves.urllib.error import HTTPError +from six.moves.urllib.parse import urlparse class upload(orig.upload): """ Override default upload behavior to obtain password in a variety of different ways. """ - def run(self): try: orig.upload.run(self) @@ -33,6 +43,141 @@ class upload(orig.upload): self._prompt_for_password() ) + def upload_file(self, command, pyversion, filename): + # Makes sure the repository URL is compliant + schema, netloc, url, params, query, fragments = \ + urlparse(self.repository) + if params or query or fragments: + raise AssertionError("Incompatible url %s" % self.repository) + + if schema not in ('http', 'https'): + raise AssertionError("unsupported schema " + schema) + + # Sign if requested + if self.sign: + gpg_args = ["gpg", "--detach-sign", "-a", filename] + if self.identity: + gpg_args[2:2] = ["--local-user", self.identity] + spawn(gpg_args, + dry_run=self.dry_run) + + # Fill in the data - send all the meta-data in case we need to + # register a new release + with open(filename, 'rb') as f: + content = f.read() + + meta = self.distribution.metadata + + data = { + # action + ':action': 'file_upload', + 'protocol_version': '1', + + # identify release + 'name': meta.get_name(), + 'version': meta.get_version(), + + # file content + 'content': (os.path.basename(filename),content), + 'filetype': command, + 'pyversion': pyversion, + 'md5_digest': hashlib.md5(content).hexdigest(), + + # additional meta-data + 'metadata_version': '1.0', + 'summary': meta.get_description(), + 'home_page': meta.get_url(), + 'author': meta.get_contact(), + 'author_email': meta.get_contact_email(), + 'license': meta.get_licence(), + 'description': meta.get_long_description(), + 'keywords': meta.get_keywords(), + 'platform': meta.get_platforms(), + 'classifiers': meta.get_classifiers(), + 'download_url': meta.get_download_url(), + # PEP 314 + 'provides': meta.get_provides(), + 'requires': meta.get_requires(), + 'obsoletes': meta.get_obsoletes(), + } + comment = '' + if command == 'bdist_rpm': + dist, version, id = platform.dist() + if dist: + comment = 'built for %s %s' % (dist, version) + elif command == 'bdist_dumb': + comment = 'built for %s' % platform.platform(terse=1) + data['comment'] = comment + + if self.sign: + data['gpg_signature'] = (os.path.basename(filename) + ".asc", + open(filename+".asc", "rb").read()) + + # set up the authentication + user_pass = (self.username + ":" + self.password).encode('ascii') + # The exact encoding of the authentication string is debated. + # Anyway PyPI only accepts ascii for both username or password. + auth = "Basic " + standard_b64encode(user_pass).decode('ascii') + + # Build up the MIME payload for the POST data + boundary = '--------------GHSKFJDLGDS7543FJKLFHRE75642756743254' + sep_boundary = b'\r\n--' + boundary.encode('ascii') + end_boundary = sep_boundary + b'--\r\n' + body = io.BytesIO() + for key, value in data.items(): + title = '\r\nContent-Disposition: form-data; name="%s"' % key + # handle multiple entries for the same name + if not isinstance(value, list): + value = [value] + for value in value: + if type(value) is tuple: + title += '; filename="%s"' % value[0] + value = value[1] + else: + value = str(value).encode('utf-8') + body.write(sep_boundary) + body.write(title.encode('utf-8')) + body.write(b"\r\n\r\n") + body.write(value) + body.write(end_boundary) + body = body.getvalue() + + msg = "Submitting %s to %s" % (filename, self.repository) + self.announce(msg, log.INFO) + + # build the Request + headers = { + 'Content-type': 'multipart/form-data; boundary=%s' % boundary, + 'Content-length': str(len(body)), + 'Authorization': auth, + } + + request = Request(self.repository, data=body, + headers=headers) + # send the data + try: + result = urlopen(request) + status = result.getcode() + reason = result.msg + except HTTPError as e: + status = e.code + reason = e.msg + except OSError as e: + self.announce(str(e), log.ERROR) + raise + + if status == 200: + self.announce('Server response (%s): %s' % (status, reason), + log.INFO) + if self.show_response: + text = self._read_pypi_response(result) + msg = '\n'.join(('-' * 75, text, '-' * 75)) + self.announce(msg, log.INFO) + else: + msg = 'Upload failed (%s): %s' % (status, reason) + self.announce(msg, log.ERROR) + raise DistutilsError(msg) + def _load_password_from_keyring(self): """ Attempt to load password from keyring. Suppress Exceptions. -- cgit v1.2.1 From 33185837dbc1f75f7894b9cbc3e56c1c6a868c4c Mon Sep 17 00:00:00 2001 From: Paul Ganssle Date: Mon, 5 Nov 2018 10:26:50 -0500 Subject: Use get_metadata_version in upload_file Previously this value was hard-coded to '1.0', which was inaccurate for many packages. Fixes #1381 --- setuptools/command/upload.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'setuptools/command/upload.py') diff --git a/setuptools/command/upload.py b/setuptools/command/upload.py index dae7d74d..01fa026c 100644 --- a/setuptools/command/upload.py +++ b/setuptools/command/upload.py @@ -84,7 +84,7 @@ class upload(orig.upload): 'md5_digest': hashlib.md5(content).hexdigest(), # additional meta-data - 'metadata_version': '1.0', + 'metadata_version': str(meta.get_metadata_version()), 'summary': meta.get_description(), 'home_page': meta.get_url(), 'author': meta.get_contact(), -- cgit v1.2.1 From b5c9c5f42db36a07dc27d39c1be2a311cc567d99 Mon Sep 17 00:00:00 2001 From: Paul Ganssle Date: Wed, 7 Nov 2018 16:23:13 -0500 Subject: Fix gpg signature code in upload_file This fixes an issue where `distutils.spawn.spawn` was not available in the ported upload_file, which is only used when signing the data. This also adds a test that the gpg signature command is invoked and included in the uploaded data. --- setuptools/command/upload.py | 1 + 1 file changed, 1 insertion(+) (limited to 'setuptools/command/upload.py') diff --git a/setuptools/command/upload.py b/setuptools/command/upload.py index 01fa026c..1851ed28 100644 --- a/setuptools/command/upload.py +++ b/setuptools/command/upload.py @@ -7,6 +7,7 @@ from base64 import standard_b64encode from distutils import log from distutils.command import upload as orig +from distutils.spawn import spawn from distutils.errors import DistutilsError -- cgit v1.2.1 From 727dd60f6a11f38d165250c543ba135687fa2e61 Mon Sep 17 00:00:00 2001 From: Paul Ganssle Date: Wed, 7 Nov 2018 16:41:19 -0500 Subject: Fix bdist_rpm and bdist_dumb in upload_file This fixes uploads when bdist_rpm or bdist_dumb are the command, both of which insert a comment about what platform they are built for. --- setuptools/command/upload.py | 1 + 1 file changed, 1 insertion(+) (limited to 'setuptools/command/upload.py') diff --git a/setuptools/command/upload.py b/setuptools/command/upload.py index 1851ed28..99d86011 100644 --- a/setuptools/command/upload.py +++ b/setuptools/command/upload.py @@ -2,6 +2,7 @@ import io import os import hashlib import getpass +import platform from base64 import standard_b64encode -- cgit v1.2.1 From fe2c9e4292699635c91174bc049aefe81bf6116c Mon Sep 17 00:00:00 2001 From: Paul Ganssle Date: Wed, 7 Nov 2018 17:07:58 -0500 Subject: Fix show_response behavior on Python 2 The `upload.show_response` feature was not added until Python 3. Rather than backport it, it is now enabled only if supported. This also adds a "smoke test" for the feature. --- setuptools/command/upload.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) (limited to 'setuptools/command/upload.py') diff --git a/setuptools/command/upload.py b/setuptools/command/upload.py index 99d86011..f57fe796 100644 --- a/setuptools/command/upload.py +++ b/setuptools/command/upload.py @@ -172,9 +172,11 @@ class upload(orig.upload): self.announce('Server response (%s): %s' % (status, reason), log.INFO) if self.show_response: - text = self._read_pypi_response(result) - msg = '\n'.join(('-' * 75, text, '-' * 75)) - self.announce(msg, log.INFO) + text = getattr(self, '_read_pypi_response', + lambda x: None)(result) + if text is not None: + msg = '\n'.join(('-' * 75, text, '-' * 75)) + self.announce(msg, log.INFO) else: msg = 'Upload failed (%s): %s' % (status, reason) self.announce(msg, log.ERROR) -- cgit v1.2.1 From 2b5b91332a01c665cab77ad7962e87525850d7f5 Mon Sep 17 00:00:00 2001 From: Paul Ganssle Date: Mon, 12 Nov 2018 10:08:55 -0500 Subject: Remove bdist_rpm and bdist_dumb comment This comment is not used anywhere and `platform.dist()` is deprecated. See CPython PR #10414: https://github.com/python/cpython/pull/10414 and bpo-35186: https://bugs.python.org/issue35186 --- setuptools/command/upload.py | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) (limited to 'setuptools/command/upload.py') diff --git a/setuptools/command/upload.py b/setuptools/command/upload.py index f57fe796..3b8cab5e 100644 --- a/setuptools/command/upload.py +++ b/setuptools/command/upload.py @@ -102,14 +102,8 @@ class upload(orig.upload): 'requires': meta.get_requires(), 'obsoletes': meta.get_obsoletes(), } - comment = '' - if command == 'bdist_rpm': - dist, version, id = platform.dist() - if dist: - comment = 'built for %s %s' % (dist, version) - elif command == 'bdist_dumb': - comment = 'built for %s' % platform.platform(terse=1) - data['comment'] = comment + + data['comment'] = '' if self.sign: data['gpg_signature'] = (os.path.basename(filename) + ".asc", -- cgit v1.2.1 From ba7698287094f7274ae7cbabaf6baedc175ac213 Mon Sep 17 00:00:00 2001 From: Oleg Sharov Date: Tue, 13 Nov 2018 12:52:43 +0400 Subject: import internal version of six --- setuptools/command/upload.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'setuptools/command/upload.py') diff --git a/setuptools/command/upload.py b/setuptools/command/upload.py index 3b8cab5e..dd17f7a9 100644 --- a/setuptools/command/upload.py +++ b/setuptools/command/upload.py @@ -12,9 +12,9 @@ from distutils.spawn import spawn from distutils.errors import DistutilsError -from six.moves.urllib.request import urlopen, Request -from six.moves.urllib.error import HTTPError -from six.moves.urllib.parse import urlparse +from setuptools.extern.six.moves.urllib.request import urlopen, Request +from setuptools.extern.six.moves.urllib.error import HTTPError +from setuptools.extern.six.moves.urllib.parse import urlparse class upload(orig.upload): """ -- cgit v1.2.1 From 0c9624fd5ee5abe3fb0d1e3dfa68a9cbaf261aed Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sat, 29 Dec 2018 13:47:21 -0500 Subject: Feed the hobgoblins (delint). --- setuptools/command/upload.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'setuptools/command/upload.py') diff --git a/setuptools/command/upload.py b/setuptools/command/upload.py index dd17f7a9..6db8888b 100644 --- a/setuptools/command/upload.py +++ b/setuptools/command/upload.py @@ -2,7 +2,6 @@ import io import os import hashlib import getpass -import platform from base64 import standard_b64encode @@ -16,6 +15,7 @@ from setuptools.extern.six.moves.urllib.request import urlopen, Request from setuptools.extern.six.moves.urllib.error import HTTPError from setuptools.extern.six.moves.urllib.parse import urlparse + class upload(orig.upload): """ Override default upload behavior to obtain password @@ -80,7 +80,7 @@ class upload(orig.upload): 'version': meta.get_version(), # file content - 'content': (os.path.basename(filename),content), + 'content': (os.path.basename(filename), content), 'filetype': command, 'pyversion': pyversion, 'md5_digest': hashlib.md5(content).hexdigest(), -- cgit v1.2.1 From f413f95e95b34b26d9ed9d9c43b3e4b3d30caecc Mon Sep 17 00:00:00 2001 From: Paul Ganssle Date: Thu, 31 Oct 2019 11:25:57 -0400 Subject: Remove "upload" and "register" commands. The upload and register commands were deprecated over a year ago, in July 2018 (PR GH-1410, discussed in issue GH-1381). It is time to actively remove them in favor of twine. --- setuptools/command/upload.py | 195 ++----------------------------------------- 1 file changed, 8 insertions(+), 187 deletions(-) (limited to 'setuptools/command/upload.py') diff --git a/setuptools/command/upload.py b/setuptools/command/upload.py index 6db8888b..ec7f81e2 100644 --- a/setuptools/command/upload.py +++ b/setuptools/command/upload.py @@ -1,196 +1,17 @@ -import io -import os -import hashlib -import getpass - -from base64 import standard_b64encode - from distutils import log from distutils.command import upload as orig -from distutils.spawn import spawn - -from distutils.errors import DistutilsError -from setuptools.extern.six.moves.urllib.request import urlopen, Request -from setuptools.extern.six.moves.urllib.error import HTTPError -from setuptools.extern.six.moves.urllib.parse import urlparse +from setuptools.errors import RemovedCommandError class upload(orig.upload): - """ - Override default upload behavior to obtain password - in a variety of different ways. - """ - def run(self): - try: - orig.upload.run(self) - finally: - self.announce( - "WARNING: Uploading via this command is deprecated, use twine " - "to upload instead (https://pypi.org/p/twine/)", - log.WARN - ) + """Formerly used to upload packages to PyPI.""" - def finalize_options(self): - orig.upload.finalize_options(self) - self.username = ( - self.username or - getpass.getuser() - ) - # Attempt to obtain password. Short circuit evaluation at the first - # sign of success. - self.password = ( - self.password or - self._load_password_from_keyring() or - self._prompt_for_password() + def run(self): + msg = ( + "The upload command has been removed, use twine to upload " + + "instead (https://pypi.org/p/twine)" ) - def upload_file(self, command, pyversion, filename): - # Makes sure the repository URL is compliant - schema, netloc, url, params, query, fragments = \ - urlparse(self.repository) - if params or query or fragments: - raise AssertionError("Incompatible url %s" % self.repository) - - if schema not in ('http', 'https'): - raise AssertionError("unsupported schema " + schema) - - # Sign if requested - if self.sign: - gpg_args = ["gpg", "--detach-sign", "-a", filename] - if self.identity: - gpg_args[2:2] = ["--local-user", self.identity] - spawn(gpg_args, - dry_run=self.dry_run) - - # Fill in the data - send all the meta-data in case we need to - # register a new release - with open(filename, 'rb') as f: - content = f.read() - - meta = self.distribution.metadata - - data = { - # action - ':action': 'file_upload', - 'protocol_version': '1', - - # identify release - 'name': meta.get_name(), - 'version': meta.get_version(), - - # file content - 'content': (os.path.basename(filename), content), - 'filetype': command, - 'pyversion': pyversion, - 'md5_digest': hashlib.md5(content).hexdigest(), - - # additional meta-data - 'metadata_version': str(meta.get_metadata_version()), - 'summary': meta.get_description(), - 'home_page': meta.get_url(), - 'author': meta.get_contact(), - 'author_email': meta.get_contact_email(), - 'license': meta.get_licence(), - 'description': meta.get_long_description(), - 'keywords': meta.get_keywords(), - 'platform': meta.get_platforms(), - 'classifiers': meta.get_classifiers(), - 'download_url': meta.get_download_url(), - # PEP 314 - 'provides': meta.get_provides(), - 'requires': meta.get_requires(), - 'obsoletes': meta.get_obsoletes(), - } - - data['comment'] = '' - - if self.sign: - data['gpg_signature'] = (os.path.basename(filename) + ".asc", - open(filename+".asc", "rb").read()) - - # set up the authentication - user_pass = (self.username + ":" + self.password).encode('ascii') - # The exact encoding of the authentication string is debated. - # Anyway PyPI only accepts ascii for both username or password. - auth = "Basic " + standard_b64encode(user_pass).decode('ascii') - - # Build up the MIME payload for the POST data - boundary = '--------------GHSKFJDLGDS7543FJKLFHRE75642756743254' - sep_boundary = b'\r\n--' + boundary.encode('ascii') - end_boundary = sep_boundary + b'--\r\n' - body = io.BytesIO() - for key, value in data.items(): - title = '\r\nContent-Disposition: form-data; name="%s"' % key - # handle multiple entries for the same name - if not isinstance(value, list): - value = [value] - for value in value: - if type(value) is tuple: - title += '; filename="%s"' % value[0] - value = value[1] - else: - value = str(value).encode('utf-8') - body.write(sep_boundary) - body.write(title.encode('utf-8')) - body.write(b"\r\n\r\n") - body.write(value) - body.write(end_boundary) - body = body.getvalue() - - msg = "Submitting %s to %s" % (filename, self.repository) - self.announce(msg, log.INFO) - - # build the Request - headers = { - 'Content-type': 'multipart/form-data; boundary=%s' % boundary, - 'Content-length': str(len(body)), - 'Authorization': auth, - } - - request = Request(self.repository, data=body, - headers=headers) - # send the data - try: - result = urlopen(request) - status = result.getcode() - reason = result.msg - except HTTPError as e: - status = e.code - reason = e.msg - except OSError as e: - self.announce(str(e), log.ERROR) - raise - - if status == 200: - self.announce('Server response (%s): %s' % (status, reason), - log.INFO) - if self.show_response: - text = getattr(self, '_read_pypi_response', - lambda x: None)(result) - if text is not None: - msg = '\n'.join(('-' * 75, text, '-' * 75)) - self.announce(msg, log.INFO) - else: - msg = 'Upload failed (%s): %s' % (status, reason) - self.announce(msg, log.ERROR) - raise DistutilsError(msg) - - def _load_password_from_keyring(self): - """ - Attempt to load password from keyring. Suppress Exceptions. - """ - try: - keyring = __import__('keyring') - return keyring.get_password(self.repository, self.username) - except Exception: - pass - - def _prompt_for_password(self): - """ - Prompt for a password on the tty. Suppress Exceptions. - """ - try: - return getpass.getpass() - except (Exception, KeyboardInterrupt): - pass + self.announce("ERROR: " + msg, log.ERROR) + raise RemovedCommandError(msg) -- cgit v1.2.1