Mercurial > repos > guerler > hhblits
comparison lib/python3.8/site-packages/setuptools/command/upload_docs.py @ 0:9e54283cc701 draft
"planemo upload commit d12c32a45bcd441307e632fca6d9af7d60289d44"
author | guerler |
---|---|
date | Mon, 27 Jul 2020 03:47:31 -0400 |
parents | |
children |
comparison
equal
deleted
inserted
replaced
-1:000000000000 | 0:9e54283cc701 |
---|---|
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 import os | |
12 import socket | |
13 import zipfile | |
14 import tempfile | |
15 import shutil | |
16 import itertools | |
17 import functools | |
18 | |
19 from setuptools.extern import six | |
20 from setuptools.extern.six.moves import http_client, urllib | |
21 | |
22 from pkg_resources import iter_entry_points | |
23 from .upload import upload | |
24 | |
25 | |
26 def _encode(s): | |
27 errors = 'strict' if six.PY2 else 'surrogateescape' | |
28 return s.encode('utf-8', errors) | |
29 | |
30 | |
31 class upload_docs(upload): | |
32 # override the default repository as upload_docs isn't | |
33 # supported by Warehouse (and won't be). | |
34 DEFAULT_REPOSITORY = 'https://pypi.python.org/pypi/' | |
35 | |
36 description = 'Upload documentation to PyPI' | |
37 | |
38 user_options = [ | |
39 ('repository=', 'r', | |
40 "url of repository [default: %s]" % upload.DEFAULT_REPOSITORY), | |
41 ('show-response', None, | |
42 'display full response text from server'), | |
43 ('upload-dir=', None, 'directory to upload'), | |
44 ] | |
45 boolean_options = upload.boolean_options | |
46 | |
47 def has_sphinx(self): | |
48 if self.upload_dir is None: | |
49 for ep in iter_entry_points('distutils.commands', 'build_sphinx'): | |
50 return True | |
51 | |
52 sub_commands = [('build_sphinx', has_sphinx)] | |
53 | |
54 def initialize_options(self): | |
55 upload.initialize_options(self) | |
56 self.upload_dir = None | |
57 self.target_dir = None | |
58 | |
59 def finalize_options(self): | |
60 upload.finalize_options(self) | |
61 if self.upload_dir is None: | |
62 if self.has_sphinx(): | |
63 build_sphinx = self.get_finalized_command('build_sphinx') | |
64 self.target_dir = build_sphinx.builder_target_dir | |
65 else: | |
66 build = self.get_finalized_command('build') | |
67 self.target_dir = os.path.join(build.build_base, 'docs') | |
68 else: | |
69 self.ensure_dirname('upload_dir') | |
70 self.target_dir = self.upload_dir | |
71 if 'pypi.python.org' in self.repository: | |
72 log.warn("Upload_docs command is deprecated. Use RTD instead.") | |
73 self.announce('Using upload directory %s' % self.target_dir) | |
74 | |
75 def create_zipfile(self, filename): | |
76 zip_file = zipfile.ZipFile(filename, "w") | |
77 try: | |
78 self.mkpath(self.target_dir) # just in case | |
79 for root, dirs, files in os.walk(self.target_dir): | |
80 if root == self.target_dir and not files: | |
81 tmpl = "no files found in upload directory '%s'" | |
82 raise DistutilsOptionError(tmpl % self.target_dir) | |
83 for name in files: | |
84 full = os.path.join(root, name) | |
85 relative = root[len(self.target_dir):].lstrip(os.path.sep) | |
86 dest = os.path.join(relative, name) | |
87 zip_file.write(full, dest) | |
88 finally: | |
89 zip_file.close() | |
90 | |
91 def run(self): | |
92 # Run sub commands | |
93 for cmd_name in self.get_sub_commands(): | |
94 self.run_command(cmd_name) | |
95 | |
96 tmp_dir = tempfile.mkdtemp() | |
97 name = self.distribution.metadata.get_name() | |
98 zip_file = os.path.join(tmp_dir, "%s.zip" % name) | |
99 try: | |
100 self.create_zipfile(zip_file) | |
101 self.upload_file(zip_file) | |
102 finally: | |
103 shutil.rmtree(tmp_dir) | |
104 | |
105 @staticmethod | |
106 def _build_part(item, sep_boundary): | |
107 key, values = item | |
108 title = '\nContent-Disposition: form-data; name="%s"' % key | |
109 # handle multiple entries for the same name | |
110 if not isinstance(values, list): | |
111 values = [values] | |
112 for value in values: | |
113 if isinstance(value, tuple): | |
114 title += '; filename="%s"' % value[0] | |
115 value = value[1] | |
116 else: | |
117 value = _encode(value) | |
118 yield sep_boundary | |
119 yield _encode(title) | |
120 yield b"\n\n" | |
121 yield value | |
122 if value and value[-1:] == b'\r': | |
123 yield b'\n' # write an extra newline (lurve Macs) | |
124 | |
125 @classmethod | |
126 def _build_multipart(cls, data): | |
127 """ | |
128 Build up the MIME payload for the POST data | |
129 """ | |
130 boundary = '--------------GHSKFJDLGDS7543FJKLFHRE75642756743254' | |
131 sep_boundary = b'\n--' + boundary.encode('ascii') | |
132 end_boundary = sep_boundary + b'--' | |
133 end_items = end_boundary, b"\n", | |
134 builder = functools.partial( | |
135 cls._build_part, | |
136 sep_boundary=sep_boundary, | |
137 ) | |
138 part_groups = map(builder, data.items()) | |
139 parts = itertools.chain.from_iterable(part_groups) | |
140 body_items = itertools.chain(parts, end_items) | |
141 content_type = 'multipart/form-data; boundary=%s' % boundary | |
142 return b''.join(body_items), content_type | |
143 | |
144 def upload_file(self, filename): | |
145 with open(filename, 'rb') as f: | |
146 content = f.read() | |
147 meta = self.distribution.metadata | |
148 data = { | |
149 ':action': 'doc_upload', | |
150 'name': meta.get_name(), | |
151 'content': (os.path.basename(filename), content), | |
152 } | |
153 # set up the authentication | |
154 credentials = _encode(self.username + ':' + self.password) | |
155 credentials = standard_b64encode(credentials) | |
156 if not six.PY2: | |
157 credentials = credentials.decode('ascii') | |
158 auth = "Basic " + credentials | |
159 | |
160 body, ct = self._build_multipart(data) | |
161 | |
162 msg = "Submitting documentation to %s" % (self.repository) | |
163 self.announce(msg, log.INFO) | |
164 | |
165 # build the Request | |
166 # We can't use urllib2 since we need to send the Basic | |
167 # auth right with the first request | |
168 schema, netloc, url, params, query, fragments = \ | |
169 urllib.parse.urlparse(self.repository) | |
170 assert not params and not query and not fragments | |
171 if schema == 'http': | |
172 conn = http_client.HTTPConnection(netloc) | |
173 elif schema == 'https': | |
174 conn = http_client.HTTPSConnection(netloc) | |
175 else: | |
176 raise AssertionError("unsupported schema " + schema) | |
177 | |
178 data = '' | |
179 try: | |
180 conn.connect() | |
181 conn.putrequest("POST", url) | |
182 content_type = ct | |
183 conn.putheader('Content-type', content_type) | |
184 conn.putheader('Content-length', str(len(body))) | |
185 conn.putheader('Authorization', auth) | |
186 conn.endheaders() | |
187 conn.send(body) | |
188 except socket.error as e: | |
189 self.announce(str(e), log.ERROR) | |
190 return | |
191 | |
192 r = conn.getresponse() | |
193 if r.status == 200: | |
194 msg = 'Server response (%s): %s' % (r.status, r.reason) | |
195 self.announce(msg, log.INFO) | |
196 elif r.status == 301: | |
197 location = r.getheader('Location') | |
198 if location is None: | |
199 location = 'https://pythonhosted.org/%s/' % meta.get_name() | |
200 msg = 'Upload successful. Visit %s' % location | |
201 self.announce(msg, log.INFO) | |
202 else: | |
203 msg = 'Upload failed (%s): %s' % (r.status, r.reason) | |
204 self.announce(msg, log.ERROR) | |
205 if self.show_response: | |
206 print('-' * 75, r.read(), '-' * 75) |