comparison venv/lib/python2.7/site-packages/setuptools/command/upload_docs.py @ 0:d67268158946 draft

planemo upload commit a3f181f5f126803c654b3a66dd4e83a48f7e203b
author bcclaywell
date Mon, 12 Oct 2015 17:43:33 -0400
parents
children
comparison
equal deleted inserted replaced
-1:000000000000 0:d67268158946
1 # -*- coding: utf-8 -*-
2 """upload_docs
3
4 Implements a Distutils 'upload_docs' subcommand (upload documentation to
5 PyPI's pythonhosted.org).
6 """
7
8 from base64 import standard_b64encode
9 from distutils import log
10 from distutils.errors import DistutilsOptionError
11 from distutils.command.upload import upload
12 import os
13 import socket
14 import zipfile
15 import tempfile
16 import sys
17 import shutil
18
19 from setuptools.compat import httplib, urlparse, unicode, iteritems, PY3
20 from pkg_resources import iter_entry_points
21
22
23 errors = 'surrogateescape' if PY3 else 'strict'
24
25
26 # This is not just a replacement for byte literals
27 # but works as a general purpose encoder
28 def b(s, encoding='utf-8'):
29 if isinstance(s, unicode):
30 return s.encode(encoding, errors)
31 return s
32
33
34 class upload_docs(upload):
35 description = 'Upload documentation to PyPI'
36
37 user_options = [
38 ('repository=', 'r',
39 "url of repository [default: %s]" % upload.DEFAULT_REPOSITORY),
40 ('show-response', None,
41 'display full response text from server'),
42 ('upload-dir=', None, 'directory to upload'),
43 ]
44 boolean_options = upload.boolean_options
45
46 def has_sphinx(self):
47 if self.upload_dir is None:
48 for ep in iter_entry_points('distutils.commands', 'build_sphinx'):
49 return True
50
51 sub_commands = [('build_sphinx', has_sphinx)]
52
53 def initialize_options(self):
54 upload.initialize_options(self)
55 self.upload_dir = None
56 self.target_dir = None
57
58 def finalize_options(self):
59 upload.finalize_options(self)
60 if self.upload_dir is None:
61 if self.has_sphinx():
62 build_sphinx = self.get_finalized_command('build_sphinx')
63 self.target_dir = build_sphinx.builder_target_dir
64 else:
65 build = self.get_finalized_command('build')
66 self.target_dir = os.path.join(build.build_base, 'docs')
67 else:
68 self.ensure_dirname('upload_dir')
69 self.target_dir = self.upload_dir
70 self.announce('Using upload directory %s' % self.target_dir)
71
72 def create_zipfile(self, filename):
73 zip_file = zipfile.ZipFile(filename, "w")
74 try:
75 self.mkpath(self.target_dir) # just in case
76 for root, dirs, files in os.walk(self.target_dir):
77 if root == self.target_dir and not files:
78 raise DistutilsOptionError(
79 "no files found in upload directory '%s'"
80 % self.target_dir)
81 for name in files:
82 full = os.path.join(root, name)
83 relative = root[len(self.target_dir):].lstrip(os.path.sep)
84 dest = os.path.join(relative, name)
85 zip_file.write(full, dest)
86 finally:
87 zip_file.close()
88
89 def run(self):
90 # Run sub commands
91 for cmd_name in self.get_sub_commands():
92 self.run_command(cmd_name)
93
94 tmp_dir = tempfile.mkdtemp()
95 name = self.distribution.metadata.get_name()
96 zip_file = os.path.join(tmp_dir, "%s.zip" % name)
97 try:
98 self.create_zipfile(zip_file)
99 self.upload_file(zip_file)
100 finally:
101 shutil.rmtree(tmp_dir)
102
103 def upload_file(self, filename):
104 f = open(filename, 'rb')
105 content = f.read()
106 f.close()
107 meta = self.distribution.metadata
108 data = {
109 ':action': 'doc_upload',
110 'name': meta.get_name(),
111 'content': (os.path.basename(filename), content),
112 }
113 # set up the authentication
114 credentials = b(self.username + ':' + self.password)
115 credentials = standard_b64encode(credentials)
116 if PY3:
117 credentials = credentials.decode('ascii')
118 auth = "Basic " + credentials
119
120 # Build up the MIME payload for the POST data
121 boundary = '--------------GHSKFJDLGDS7543FJKLFHRE75642756743254'
122 sep_boundary = b('\n--') + b(boundary)
123 end_boundary = sep_boundary + b('--')
124 body = []
125 for key, values in iteritems(data):
126 title = '\nContent-Disposition: form-data; name="%s"' % key
127 # handle multiple entries for the same name
128 if not isinstance(values, list):
129 values = [values]
130 for value in values:
131 if type(value) is tuple:
132 title += '; filename="%s"' % value[0]
133 value = value[1]
134 else:
135 value = b(value)
136 body.append(sep_boundary)
137 body.append(b(title))
138 body.append(b("\n\n"))
139 body.append(value)
140 if value and value[-1:] == b('\r'):
141 body.append(b('\n')) # write an extra newline (lurve Macs)
142 body.append(end_boundary)
143 body.append(b("\n"))
144 body = b('').join(body)
145
146 self.announce("Submitting documentation to %s" % (self.repository),
147 log.INFO)
148
149 # build the Request
150 # We can't use urllib2 since we need to send the Basic
151 # auth right with the first request
152 schema, netloc, url, params, query, fragments = \
153 urlparse(self.repository)
154 assert not params and not query and not fragments
155 if schema == 'http':
156 conn = httplib.HTTPConnection(netloc)
157 elif schema == 'https':
158 conn = httplib.HTTPSConnection(netloc)
159 else:
160 raise AssertionError("unsupported schema " + schema)
161
162 data = ''
163 try:
164 conn.connect()
165 conn.putrequest("POST", url)
166 content_type = 'multipart/form-data; boundary=%s' % boundary
167 conn.putheader('Content-type', content_type)
168 conn.putheader('Content-length', str(len(body)))
169 conn.putheader('Authorization', auth)
170 conn.endheaders()
171 conn.send(body)
172 except socket.error as e:
173 self.announce(str(e), log.ERROR)
174 return
175
176 r = conn.getresponse()
177 if r.status == 200:
178 self.announce('Server response (%s): %s' % (r.status, r.reason),
179 log.INFO)
180 elif r.status == 301:
181 location = r.getheader('Location')
182 if location is None:
183 location = 'https://pythonhosted.org/%s/' % meta.get_name()
184 self.announce('Upload successful. Visit %s' % location,
185 log.INFO)
186 else:
187 self.announce('Upload failed (%s): %s' % (r.status, r.reason),
188 log.ERROR)
189 if self.show_response:
190 print('-' * 75, r.read(), '-' * 75)