Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(344)

Side by Side Diff: recipe_engine/third_party/setuptools/command/upload_docs.py

Issue 1344583003: Recipe package system. (Closed) Base URL: git@github.com:luci/recipes-py.git@master
Patch Set: Recompiled proto Created 5 years, 3 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
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:
173 e = sys.exc_info()[1]
174 self.announce(str(e), log.ERROR)
175 return
176
177 r = conn.getresponse()
178 if r.status == 200:
179 self.announce('Server response (%s): %s' % (r.status, r.reason),
180 log.INFO)
181 elif r.status == 301:
182 location = r.getheader('Location')
183 if location is None:
184 location = 'https://pythonhosted.org/%s/' % meta.get_name()
185 self.announce('Upload successful. Visit %s' % location,
186 log.INFO)
187 else:
188 self.announce('Upload failed (%s): %s' % (r.status, r.reason),
189 log.ERROR)
190 if self.show_response:
191 print('-' * 75, r.read(), '-' * 75)
OLDNEW
« no previous file with comments | « recipe_engine/third_party/setuptools/command/test.py ('k') | recipe_engine/third_party/setuptools/compat.py » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698