root / env / lib / python2.7 / site-packages / distribute-0.6.19-py2.7.egg / setuptools / command / upload_docs.py @ 1a305335
History | View | Annotate | Download (6.03 KB)
1 | 1a305335 | officers | # -*- coding: utf-8 -*-
|
---|---|---|---|
2 | """upload_docs
|
||
3 |
|
||
4 | Implements a Distutils 'upload_docs' subcommand (upload documentation to
|
||
5 | PyPI's packages.python.org).
|
||
6 | """
|
||
7 | |||
8 | import os |
||
9 | import socket |
||
10 | import zipfile |
||
11 | import httplib |
||
12 | import base64 |
||
13 | import urlparse |
||
14 | import tempfile |
||
15 | import sys |
||
16 | |||
17 | from distutils import log |
||
18 | from distutils.errors import DistutilsOptionError |
||
19 | |||
20 | try:
|
||
21 | from distutils.command.upload import upload |
||
22 | except ImportError: |
||
23 | from setuptools.command.upload import upload |
||
24 | |||
25 | _IS_PYTHON3 = sys.version > '3'
|
||
26 | |||
27 | try:
|
||
28 | bytes
|
||
29 | except NameError: |
||
30 | bytes = str
|
||
31 | |||
32 | def b(str_or_bytes): |
||
33 | """Return bytes by either encoding the argument as ASCII or simply return
|
||
34 | the argument as-is."""
|
||
35 | if not isinstance(str_or_bytes, bytes): |
||
36 | return str_or_bytes.encode('ascii') |
||
37 | else:
|
||
38 | return str_or_bytes
|
||
39 | |||
40 | |||
41 | class upload_docs(upload): |
||
42 | |||
43 | description = 'Upload documentation to PyPI'
|
||
44 | |||
45 | user_options = [ |
||
46 | ('repository=', 'r', |
||
47 | "url of repository [default: %s]" % upload.DEFAULT_REPOSITORY),
|
||
48 | ('show-response', None, |
||
49 | 'display full response text from server'),
|
||
50 | ('upload-dir=', None, 'directory to upload'), |
||
51 | ] |
||
52 | boolean_options = upload.boolean_options |
||
53 | |||
54 | def initialize_options(self): |
||
55 | upload.initialize_options(self)
|
||
56 | self.upload_dir = None |
||
57 | |||
58 | def finalize_options(self): |
||
59 | upload.finalize_options(self)
|
||
60 | if self.upload_dir is None: |
||
61 | build = self.get_finalized_command('build') |
||
62 | self.upload_dir = os.path.join(build.build_base, 'docs') |
||
63 | self.mkpath(self.upload_dir) |
||
64 | self.ensure_dirname('upload_dir') |
||
65 | self.announce('Using upload directory %s' % self.upload_dir) |
||
66 | |||
67 | def create_zipfile(self): |
||
68 | name = self.distribution.metadata.get_name()
|
||
69 | tmp_dir = tempfile.mkdtemp() |
||
70 | tmp_file = os.path.join(tmp_dir, "%s.zip" % name)
|
||
71 | zip_file = zipfile.ZipFile(tmp_file, "w")
|
||
72 | for root, dirs, files in os.walk(self.upload_dir): |
||
73 | if root == self.upload_dir and not files: |
||
74 | raise DistutilsOptionError(
|
||
75 | "no files found in upload directory '%s'"
|
||
76 | % self.upload_dir)
|
||
77 | for name in files: |
||
78 | full = os.path.join(root, name) |
||
79 | relative = root[len(self.upload_dir):].lstrip(os.path.sep) |
||
80 | dest = os.path.join(relative, name) |
||
81 | zip_file.write(full, dest) |
||
82 | zip_file.close() |
||
83 | return tmp_file
|
||
84 | |||
85 | def run(self): |
||
86 | zip_file = self.create_zipfile()
|
||
87 | self.upload_file(zip_file)
|
||
88 | |||
89 | def upload_file(self, filename): |
||
90 | content = open(filename, 'rb').read() |
||
91 | meta = self.distribution.metadata
|
||
92 | data = { |
||
93 | ':action': 'doc_upload', |
||
94 | 'name': meta.get_name(),
|
||
95 | 'content': (os.path.basename(filename), content),
|
||
96 | } |
||
97 | # set up the authentication
|
||
98 | credentials = self.username + ':' + self.password |
||
99 | if _IS_PYTHON3: # base64 only works with bytes in Python 3. |
||
100 | encoded_creds = base64.encodebytes(credentials.encode('utf8'))
|
||
101 | auth = bytes("Basic ") |
||
102 | else:
|
||
103 | encoded_creds = base64.encodestring(credentials) |
||
104 | auth = "Basic "
|
||
105 | auth += encoded_creds.strip() |
||
106 | |||
107 | # Build up the MIME payload for the POST data
|
||
108 | boundary = b('--------------GHSKFJDLGDS7543FJKLFHRE75642756743254')
|
||
109 | sep_boundary = b('\n--') + boundary
|
||
110 | end_boundary = sep_boundary + b('--')
|
||
111 | body = [] |
||
112 | for key, values in data.items(): |
||
113 | # handle multiple entries for the same name
|
||
114 | if type(values) != type([]): |
||
115 | values = [values] |
||
116 | for value in values: |
||
117 | if type(value) is tuple: |
||
118 | fn = b(';filename="%s"' % value[0]) |
||
119 | value = value[1]
|
||
120 | else:
|
||
121 | fn = b("")
|
||
122 | body.append(sep_boundary) |
||
123 | body.append(b('\nContent-Disposition: form-data; name="%s"'%key))
|
||
124 | body.append(fn) |
||
125 | body.append(b("\n\n"))
|
||
126 | body.append(b(value)) |
||
127 | if value and value[-1] == b('\r'): |
||
128 | body.append(b('\n')) # write an extra newline (lurve Macs) |
||
129 | body.append(end_boundary) |
||
130 | body.append(b("\n"))
|
||
131 | body = b('').join(body)
|
||
132 | |||
133 | self.announce("Submitting documentation to %s" % (self.repository), |
||
134 | log.INFO) |
||
135 | |||
136 | # build the Request
|
||
137 | # We can't use urllib2 since we need to send the Basic
|
||
138 | # auth right with the first request
|
||
139 | schema, netloc, url, params, query, fragments = \ |
||
140 | urlparse.urlparse(self.repository)
|
||
141 | assert not params and not query and not fragments |
||
142 | if schema == 'http': |
||
143 | conn = httplib.HTTPConnection(netloc) |
||
144 | elif schema == 'https': |
||
145 | conn = httplib.HTTPSConnection(netloc) |
||
146 | else:
|
||
147 | raise AssertionError("unsupported schema "+schema) |
||
148 | |||
149 | data = ''
|
||
150 | loglevel = log.INFO |
||
151 | try:
|
||
152 | conn.connect() |
||
153 | conn.putrequest("POST", url)
|
||
154 | conn.putheader('Content-type',
|
||
155 | 'multipart/form-data; boundary=%s'%boundary)
|
||
156 | conn.putheader('Content-length', str(len(body))) |
||
157 | conn.putheader('Authorization', auth)
|
||
158 | conn.endheaders() |
||
159 | conn.send(body) |
||
160 | except socket.error, e:
|
||
161 | self.announce(str(e), log.ERROR) |
||
162 | return
|
||
163 | |||
164 | r = conn.getresponse() |
||
165 | if r.status == 200: |
||
166 | self.announce('Server response (%s): %s' % (r.status, r.reason), |
||
167 | log.INFO) |
||
168 | elif r.status == 301: |
||
169 | location = r.getheader('Location')
|
||
170 | if location is None: |
||
171 | location = 'http://packages.python.org/%s/' % meta.get_name()
|
||
172 | self.announce('Upload successful. Visit %s' % location, |
||
173 | log.INFO) |
||
174 | else:
|
||
175 | self.announce('Upload failed (%s): %s' % (r.status, r.reason), |
||
176 | log.ERROR) |
||
177 | if self.show_response: |
||
178 | print '-'*75, r.read(), '-'*75 |