Project

General

Profile

Statistics
| Branch: | Revision:

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
# -*- 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