ยปCore Development>Code coverage>Lib/packaging/command/upload_docs.py

Python code coverage for Lib/packaging/command/upload_docs.py

#countcontent
1n/a"""Upload HTML documentation to a project index."""
2n/a
3n/aimport os
4n/aimport base64
5n/aimport socket
6n/aimport zipfile
7n/aimport logging
8n/aimport http.client
9n/aimport urllib.parse
10n/afrom io import BytesIO
11n/a
12n/afrom packaging import logger
13n/afrom packaging.util import (read_pypirc, DEFAULT_REPOSITORY, DEFAULT_REALM,
14n/a encode_multipart)
15n/afrom packaging.errors import PackagingFileError
16n/afrom packaging.command.cmd import Command
17n/a
18n/a
19n/adef zip_dir(directory):
20n/a """Compresses recursively contents of directory into a BytesIO object"""
21n/a destination = BytesIO()
22n/a with zipfile.ZipFile(destination, "w") as zip_file:
23n/a for root, dirs, files in os.walk(directory):
24n/a for name in files:
25n/a full = os.path.join(root, name)
26n/a relative = root[len(directory):].lstrip(os.path.sep)
27n/a dest = os.path.join(relative, name)
28n/a zip_file.write(full, dest)
29n/a return destination
30n/a
31n/a
32n/aclass upload_docs(Command):
33n/a
34n/a description = "upload HTML documentation to PyPI"
35n/a
36n/a user_options = [
37n/a ('repository=', 'r',
38n/a "repository URL [default: %s]" % DEFAULT_REPOSITORY),
39n/a ('show-response', None,
40n/a "display full response text from server"),
41n/a ('upload-dir=', None,
42n/a "directory to upload"),
43n/a ]
44n/a
45n/a def initialize_options(self):
46n/a self.repository = None
47n/a self.realm = None
48n/a self.show_response = False
49n/a self.upload_dir = None
50n/a self.username = ''
51n/a self.password = ''
52n/a
53n/a def finalize_options(self):
54n/a if self.repository is None:
55n/a self.repository = DEFAULT_REPOSITORY
56n/a if self.realm is None:
57n/a self.realm = DEFAULT_REALM
58n/a if self.upload_dir is None:
59n/a build = self.get_finalized_command('build')
60n/a self.upload_dir = os.path.join(build.build_base, "docs")
61n/a if not os.path.isdir(self.upload_dir):
62n/a self.upload_dir = os.path.join(build.build_base, "doc")
63n/a logger.info('Using upload directory %s', self.upload_dir)
64n/a self.verify_upload_dir(self.upload_dir)
65n/a config = read_pypirc(self.repository, self.realm)
66n/a if config != {}:
67n/a self.username = config['username']
68n/a self.password = config['password']
69n/a self.repository = config['repository']
70n/a self.realm = config['realm']
71n/a
72n/a def verify_upload_dir(self, upload_dir):
73n/a self.ensure_dirname('upload_dir')
74n/a index_location = os.path.join(upload_dir, "index.html")
75n/a if not os.path.exists(index_location):
76n/a mesg = "No 'index.html found in docs directory (%s)"
77n/a raise PackagingFileError(mesg % upload_dir)
78n/a
79n/a def run(self):
80n/a name = self.distribution.metadata['Name']
81n/a version = self.distribution.metadata['Version']
82n/a zip_file = zip_dir(self.upload_dir)
83n/a
84n/a fields = [(':action', 'doc_upload'),
85n/a ('name', name), ('version', version)]
86n/a files = [('content', name, zip_file.getvalue())]
87n/a content_type, body = encode_multipart(fields, files)
88n/a
89n/a credentials = self.username + ':' + self.password
90n/a # FIXME should use explicit encoding
91n/a auth = b"Basic " + base64.encodebytes(credentials.encode()).strip()
92n/a
93n/a logger.info("Submitting documentation to %s", self.repository)
94n/a
95n/a scheme, netloc, url, params, query, fragments = urllib.parse.urlparse(
96n/a self.repository)
97n/a if scheme == "http":
98n/a conn = http.client.HTTPConnection(netloc)
99n/a elif scheme == "https":
100n/a conn = http.client.HTTPSConnection(netloc)
101n/a else:
102n/a raise AssertionError("unsupported scheme %r" % scheme)
103n/a
104n/a try:
105n/a conn.connect()
106n/a conn.putrequest("POST", url)
107n/a conn.putheader('Content-type', content_type)
108n/a conn.putheader('Content-length', str(len(body)))
109n/a conn.putheader('Authorization', auth)
110n/a conn.endheaders()
111n/a conn.send(body)
112n/a
113n/a except socket.error as e:
114n/a logger.error(e)
115n/a return
116n/a
117n/a r = conn.getresponse()
118n/a
119n/a if r.status == 200:
120n/a logger.info('Server response (%s): %s', r.status, r.reason)
121n/a elif r.status == 301:
122n/a location = r.getheader('Location')
123n/a if location is None:
124n/a location = 'http://packages.python.org/%s/' % name
125n/a logger.info('Upload successful. Visit %s', location)
126n/a else:
127n/a logger.error('Upload failed (%s): %s', r.status, r.reason)
128n/a
129n/a if self.show_response and logger.isEnabledFor(logging.INFO):
130n/a sep = '-' * 75
131n/a logger.info('%s\n%s\n%s', sep, r.read().decode('utf-8'), sep)