root / env / lib / python2.7 / site-packages / distribute-0.6.19-py2.7.egg / setuptools / command / upload.py @ 1a305335
History | View | Annotate | Download (6.52 KB)
1 | 1a305335 | officers | """distutils.command.upload
|
---|---|---|---|
2 |
|
||
3 | Implements the Distutils 'upload' subcommand (upload package to PyPI)."""
|
||
4 | |||
5 | from distutils.errors import * |
||
6 | from distutils.core import Command |
||
7 | from distutils.spawn import spawn |
||
8 | from distutils import log |
||
9 | try:
|
||
10 | from hashlib import md5 |
||
11 | except ImportError: |
||
12 | from md5 import md5 |
||
13 | import os |
||
14 | import socket |
||
15 | import platform |
||
16 | import ConfigParser |
||
17 | import httplib |
||
18 | import base64 |
||
19 | import urlparse |
||
20 | import cStringIO as StringIO |
||
21 | |||
22 | class upload(Command): |
||
23 | |||
24 | description = "upload binary package to PyPI"
|
||
25 | |||
26 | DEFAULT_REPOSITORY = 'http://pypi.python.org/pypi'
|
||
27 | |||
28 | user_options = [ |
||
29 | ('repository=', 'r', |
||
30 | "url of repository [default: %s]" % DEFAULT_REPOSITORY),
|
||
31 | ('show-response', None, |
||
32 | 'display full response text from server'),
|
||
33 | ('sign', 's', |
||
34 | 'sign files to upload using gpg'),
|
||
35 | ('identity=', 'i', 'GPG identity used to sign files'), |
||
36 | ] |
||
37 | boolean_options = ['show-response', 'sign'] |
||
38 | |||
39 | def initialize_options(self): |
||
40 | self.username = '' |
||
41 | self.password = '' |
||
42 | self.repository = '' |
||
43 | self.show_response = 0 |
||
44 | self.sign = False |
||
45 | self.identity = None |
||
46 | |||
47 | def finalize_options(self): |
||
48 | if self.identity and not self.sign: |
||
49 | raise DistutilsOptionError(
|
||
50 | "Must use --sign for --identity to have meaning"
|
||
51 | ) |
||
52 | if os.environ.has_key('HOME'): |
||
53 | rc = os.path.join(os.environ['HOME'], '.pypirc') |
||
54 | if os.path.exists(rc):
|
||
55 | self.announce('Using PyPI login from %s' % rc) |
||
56 | config = ConfigParser.ConfigParser({ |
||
57 | 'username':'', |
||
58 | 'password':'', |
||
59 | 'repository':''}) |
||
60 | config.read(rc) |
||
61 | if not self.repository: |
||
62 | self.repository = config.get('server-login', 'repository') |
||
63 | if not self.username: |
||
64 | self.username = config.get('server-login', 'username') |
||
65 | if not self.password: |
||
66 | self.password = config.get('server-login', 'password') |
||
67 | if not self.repository: |
||
68 | self.repository = self.DEFAULT_REPOSITORY |
||
69 | |||
70 | def run(self): |
||
71 | if not self.distribution.dist_files: |
||
72 | raise DistutilsOptionError("No dist file created in earlier command") |
||
73 | for command, pyversion, filename in self.distribution.dist_files: |
||
74 | self.upload_file(command, pyversion, filename)
|
||
75 | |||
76 | def upload_file(self, command, pyversion, filename): |
||
77 | # Sign if requested
|
||
78 | if self.sign: |
||
79 | gpg_args = ["gpg", "--detach-sign", "-a", filename] |
||
80 | if self.identity: |
||
81 | gpg_args[2:2] = ["--local-user", self.identity] |
||
82 | spawn(gpg_args, |
||
83 | dry_run=self.dry_run)
|
||
84 | |||
85 | # Fill in the data
|
||
86 | f = open(filename,'rb') |
||
87 | content = f.read() |
||
88 | f.close() |
||
89 | basename = os.path.basename(filename) |
||
90 | comment = ''
|
||
91 | if command=='bdist_egg' and self.distribution.has_ext_modules(): |
||
92 | comment = "built on %s" % platform.platform(terse=1) |
||
93 | data = { |
||
94 | ':action':'file_upload', |
||
95 | 'protcol_version':'1', |
||
96 | 'name':self.distribution.get_name(), |
||
97 | 'version':self.distribution.get_version(), |
||
98 | 'content':(basename,content),
|
||
99 | 'filetype':command,
|
||
100 | 'pyversion':pyversion,
|
||
101 | 'md5_digest':md5(content).hexdigest(),
|
||
102 | } |
||
103 | if command == 'bdist_rpm': |
||
104 | dist, version, id = platform.dist() |
||
105 | if dist:
|
||
106 | comment = 'built for %s %s' % (dist, version)
|
||
107 | elif command == 'bdist_dumb': |
||
108 | comment = 'built for %s' % platform.platform(terse=1) |
||
109 | data['comment'] = comment
|
||
110 | |||
111 | if self.sign: |
||
112 | data['gpg_signature'] = (os.path.basename(filename) + ".asc", |
||
113 | open(filename+".asc").read()) |
||
114 | |||
115 | # set up the authentication
|
||
116 | auth = "Basic " + base64.encodestring(self.username + ":" + self.password).strip() |
||
117 | |||
118 | # Build up the MIME payload for the POST data
|
||
119 | boundary = '--------------GHSKFJDLGDS7543FJKLFHRE75642756743254'
|
||
120 | sep_boundary = '\n--' + boundary
|
||
121 | end_boundary = sep_boundary + '--'
|
||
122 | body = StringIO.StringIO() |
||
123 | for key, value in data.items(): |
||
124 | # handle multiple entries for the same name
|
||
125 | if type(value) != type([]): |
||
126 | value = [value] |
||
127 | for value in value: |
||
128 | if type(value) is tuple: |
||
129 | fn = ';filename="%s"' % value[0] |
||
130 | value = value[1]
|
||
131 | else:
|
||
132 | fn = ""
|
||
133 | value = str(value)
|
||
134 | body.write(sep_boundary) |
||
135 | body.write('\nContent-Disposition: form-data; name="%s"'%key)
|
||
136 | body.write(fn) |
||
137 | body.write("\n\n")
|
||
138 | body.write(value) |
||
139 | if value and value[-1] == '\r': |
||
140 | body.write('\n') # write an extra newline (lurve Macs) |
||
141 | body.write(end_boundary) |
||
142 | body.write("\n")
|
||
143 | body = body.getvalue() |
||
144 | |||
145 | self.announce("Submitting %s to %s" % (filename, self.repository), log.INFO) |
||
146 | |||
147 | # build the Request
|
||
148 | # We can't use urllib2 since we need to send the Basic
|
||
149 | # auth right with the first request
|
||
150 | schema, netloc, url, params, query, fragments = \ |
||
151 | urlparse.urlparse(self.repository)
|
||
152 | assert not params and not query and not fragments |
||
153 | if schema == 'http': |
||
154 | http = httplib.HTTPConnection(netloc) |
||
155 | elif schema == 'https': |
||
156 | http = httplib.HTTPSConnection(netloc) |
||
157 | else:
|
||
158 | raise AssertionError, "unsupported schema "+schema |
||
159 | |||
160 | data = ''
|
||
161 | loglevel = log.INFO |
||
162 | try:
|
||
163 | http.connect() |
||
164 | http.putrequest("POST", url)
|
||
165 | http.putheader('Content-type',
|
||
166 | 'multipart/form-data; boundary=%s'%boundary)
|
||
167 | http.putheader('Content-length', str(len(body))) |
||
168 | http.putheader('Authorization', auth)
|
||
169 | http.endheaders() |
||
170 | http.send(body) |
||
171 | except socket.error, e:
|
||
172 | self.announce(str(e), log.ERROR) |
||
173 | return
|
||
174 | |||
175 | r = http.getresponse() |
||
176 | if r.status == 200: |
||
177 | self.announce('Server response (%s): %s' % (r.status, r.reason), |
||
178 | log.INFO) |
||
179 | else:
|
||
180 | self.announce('Upload failed (%s): %s' % (r.status, r.reason), |
||
181 | log.ERROR) |
||
182 | if self.show_response: |
||
183 | print '-'*75, r.read(), '-'*75 |