Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
keewenaw
GitHub Repository: keewenaw/ethereum-wallet-cracker
Path: blob/main/test/lib/python3.9/site-packages/setuptools/_distutils/command/upload.py
4804 views
1
"""
2
distutils.command.upload
3
4
Implements the Distutils 'upload' subcommand (upload package to a package
5
index).
6
"""
7
8
import os
9
import io
10
import hashlib
11
from base64 import standard_b64encode
12
from urllib.request import urlopen, Request, HTTPError
13
from urllib.parse import urlparse
14
from distutils.errors import DistutilsError, DistutilsOptionError
15
from distutils.core import PyPIRCCommand
16
from distutils.spawn import spawn
17
from distutils import log
18
19
20
# PyPI Warehouse supports MD5, SHA256, and Blake2 (blake2-256)
21
# https://bugs.python.org/issue40698
22
_FILE_CONTENT_DIGESTS = {
23
"md5_digest": getattr(hashlib, "md5", None),
24
"sha256_digest": getattr(hashlib, "sha256", None),
25
"blake2_256_digest": getattr(hashlib, "blake2b", None),
26
}
27
28
29
class upload(PyPIRCCommand):
30
31
description = "upload binary package to PyPI"
32
33
user_options = PyPIRCCommand.user_options + [
34
('sign', 's',
35
'sign files to upload using gpg'),
36
('identity=', 'i', 'GPG identity used to sign files'),
37
]
38
39
boolean_options = PyPIRCCommand.boolean_options + ['sign']
40
41
def initialize_options(self):
42
PyPIRCCommand.initialize_options(self)
43
self.username = ''
44
self.password = ''
45
self.show_response = 0
46
self.sign = False
47
self.identity = None
48
49
def finalize_options(self):
50
PyPIRCCommand.finalize_options(self)
51
if self.identity and not self.sign:
52
raise DistutilsOptionError(
53
"Must use --sign for --identity to have meaning"
54
)
55
config = self._read_pypirc()
56
if config != {}:
57
self.username = config['username']
58
self.password = config['password']
59
self.repository = config['repository']
60
self.realm = config['realm']
61
62
# getting the password from the distribution
63
# if previously set by the register command
64
if not self.password and self.distribution.password:
65
self.password = self.distribution.password
66
67
def run(self):
68
if not self.distribution.dist_files:
69
msg = ("Must create and upload files in one command "
70
"(e.g. setup.py sdist upload)")
71
raise DistutilsOptionError(msg)
72
for command, pyversion, filename in self.distribution.dist_files:
73
self.upload_file(command, pyversion, filename)
74
75
def upload_file(self, command, pyversion, filename):
76
# Makes sure the repository URL is compliant
77
schema, netloc, url, params, query, fragments = \
78
urlparse(self.repository)
79
if params or query or fragments:
80
raise AssertionError("Incompatible url %s" % self.repository)
81
82
if schema not in ('http', 'https'):
83
raise AssertionError("unsupported schema " + schema)
84
85
# Sign if requested
86
if self.sign:
87
gpg_args = ["gpg", "--detach-sign", "-a", filename]
88
if self.identity:
89
gpg_args[2:2] = ["--local-user", self.identity]
90
spawn(gpg_args,
91
dry_run=self.dry_run)
92
93
# Fill in the data - send all the meta-data in case we need to
94
# register a new release
95
f = open(filename,'rb')
96
try:
97
content = f.read()
98
finally:
99
f.close()
100
101
meta = self.distribution.metadata
102
data = {
103
# action
104
':action': 'file_upload',
105
'protocol_version': '1',
106
107
# identify release
108
'name': meta.get_name(),
109
'version': meta.get_version(),
110
111
# file content
112
'content': (os.path.basename(filename),content),
113
'filetype': command,
114
'pyversion': pyversion,
115
116
# additional meta-data
117
'metadata_version': '1.0',
118
'summary': meta.get_description(),
119
'home_page': meta.get_url(),
120
'author': meta.get_contact(),
121
'author_email': meta.get_contact_email(),
122
'license': meta.get_licence(),
123
'description': meta.get_long_description(),
124
'keywords': meta.get_keywords(),
125
'platform': meta.get_platforms(),
126
'classifiers': meta.get_classifiers(),
127
'download_url': meta.get_download_url(),
128
# PEP 314
129
'provides': meta.get_provides(),
130
'requires': meta.get_requires(),
131
'obsoletes': meta.get_obsoletes(),
132
}
133
134
data['comment'] = ''
135
136
# file content digests
137
for digest_name, digest_cons in _FILE_CONTENT_DIGESTS.items():
138
if digest_cons is None:
139
continue
140
try:
141
data[digest_name] = digest_cons(content).hexdigest()
142
except ValueError:
143
# hash digest not available or blocked by security policy
144
pass
145
146
if self.sign:
147
with open(filename + ".asc", "rb") as f:
148
data['gpg_signature'] = (os.path.basename(filename) + ".asc",
149
f.read())
150
151
# set up the authentication
152
user_pass = (self.username + ":" + self.password).encode('ascii')
153
# The exact encoding of the authentication string is debated.
154
# Anyway PyPI only accepts ascii for both username or password.
155
auth = "Basic " + standard_b64encode(user_pass).decode('ascii')
156
157
# Build up the MIME payload for the POST data
158
boundary = '--------------GHSKFJDLGDS7543FJKLFHRE75642756743254'
159
sep_boundary = b'\r\n--' + boundary.encode('ascii')
160
end_boundary = sep_boundary + b'--\r\n'
161
body = io.BytesIO()
162
for key, value in data.items():
163
title = '\r\nContent-Disposition: form-data; name="%s"' % key
164
# handle multiple entries for the same name
165
if not isinstance(value, list):
166
value = [value]
167
for value in value:
168
if type(value) is tuple:
169
title += '; filename="%s"' % value[0]
170
value = value[1]
171
else:
172
value = str(value).encode('utf-8')
173
body.write(sep_boundary)
174
body.write(title.encode('utf-8'))
175
body.write(b"\r\n\r\n")
176
body.write(value)
177
body.write(end_boundary)
178
body = body.getvalue()
179
180
msg = "Submitting %s to %s" % (filename, self.repository)
181
self.announce(msg, log.INFO)
182
183
# build the Request
184
headers = {
185
'Content-type': 'multipart/form-data; boundary=%s' % boundary,
186
'Content-length': str(len(body)),
187
'Authorization': auth,
188
}
189
190
request = Request(self.repository, data=body,
191
headers=headers)
192
# send the data
193
try:
194
result = urlopen(request)
195
status = result.getcode()
196
reason = result.msg
197
except HTTPError as e:
198
status = e.code
199
reason = e.msg
200
except OSError as e:
201
self.announce(str(e), log.ERROR)
202
raise
203
204
if status == 200:
205
self.announce('Server response (%s): %s' % (status, reason),
206
log.INFO)
207
if self.show_response:
208
text = self._read_pypi_response(result)
209
msg = '\n'.join(('-' * 75, text, '-' * 75))
210
self.announce(msg, log.INFO)
211
else:
212
msg = 'Upload failed (%s): %s' % (status, reason)
213
self.announce(msg, log.ERROR)
214
raise DistutilsError(msg)
215
216