ยปCore Development>Code coverage>Lib/distutils/command/register.py

Python code coverage for Lib/distutils/command/register.py

#countcontent
1n/a"""distutils.command.register
2n/a
3n/aImplements the Distutils 'register' command (register with the repository).
4n/a"""
5n/a
6n/a# created 2002/10/21, Richard Jones
7n/a
8n/aimport getpass
9n/aimport io
10n/aimport urllib.parse, urllib.request
11n/afrom warnings import warn
12n/a
13n/afrom distutils.core import PyPIRCCommand
14n/afrom distutils.errors import *
15n/afrom distutils import log
16n/a
17n/aclass register(PyPIRCCommand):
18n/a
19n/a description = ("register the distribution with the Python package index")
20n/a user_options = PyPIRCCommand.user_options + [
21n/a ('list-classifiers', None,
22n/a 'list the valid Trove classifiers'),
23n/a ('strict', None ,
24n/a 'Will stop the registering if the meta-data are not fully compliant')
25n/a ]
26n/a boolean_options = PyPIRCCommand.boolean_options + [
27n/a 'verify', 'list-classifiers', 'strict']
28n/a
29n/a sub_commands = [('check', lambda self: True)]
30n/a
31n/a def initialize_options(self):
32n/a PyPIRCCommand.initialize_options(self)
33n/a self.list_classifiers = 0
34n/a self.strict = 0
35n/a
36n/a def finalize_options(self):
37n/a PyPIRCCommand.finalize_options(self)
38n/a # setting options for the `check` subcommand
39n/a check_options = {'strict': ('register', self.strict),
40n/a 'restructuredtext': ('register', 1)}
41n/a self.distribution.command_options['check'] = check_options
42n/a
43n/a def run(self):
44n/a self.finalize_options()
45n/a self._set_config()
46n/a
47n/a # Run sub commands
48n/a for cmd_name in self.get_sub_commands():
49n/a self.run_command(cmd_name)
50n/a
51n/a if self.dry_run:
52n/a self.verify_metadata()
53n/a elif self.list_classifiers:
54n/a self.classifiers()
55n/a else:
56n/a self.send_metadata()
57n/a
58n/a def check_metadata(self):
59n/a """Deprecated API."""
60n/a warn("distutils.command.register.check_metadata is deprecated, \
61n/a use the check command instead", PendingDeprecationWarning)
62n/a check = self.distribution.get_command_obj('check')
63n/a check.ensure_finalized()
64n/a check.strict = self.strict
65n/a check.restructuredtext = 1
66n/a check.run()
67n/a
68n/a def _set_config(self):
69n/a ''' Reads the configuration file and set attributes.
70n/a '''
71n/a config = self._read_pypirc()
72n/a if config != {}:
73n/a self.username = config['username']
74n/a self.password = config['password']
75n/a self.repository = config['repository']
76n/a self.realm = config['realm']
77n/a self.has_config = True
78n/a else:
79n/a if self.repository not in ('pypi', self.DEFAULT_REPOSITORY):
80n/a raise ValueError('%s not found in .pypirc' % self.repository)
81n/a if self.repository == 'pypi':
82n/a self.repository = self.DEFAULT_REPOSITORY
83n/a self.has_config = False
84n/a
85n/a def classifiers(self):
86n/a ''' Fetch the list of classifiers from the server.
87n/a '''
88n/a url = self.repository+'?:action=list_classifiers'
89n/a response = urllib.request.urlopen(url)
90n/a log.info(self._read_pypi_response(response))
91n/a
92n/a def verify_metadata(self):
93n/a ''' Send the metadata to the package index server to be checked.
94n/a '''
95n/a # send the info to the server and report the result
96n/a (code, result) = self.post_to_server(self.build_post_data('verify'))
97n/a log.info('Server response (%s): %s', code, result)
98n/a
99n/a def send_metadata(self):
100n/a ''' Send the metadata to the package index server.
101n/a
102n/a Well, do the following:
103n/a 1. figure who the user is, and then
104n/a 2. send the data as a Basic auth'ed POST.
105n/a
106n/a First we try to read the username/password from $HOME/.pypirc,
107n/a which is a ConfigParser-formatted file with a section
108n/a [distutils] containing username and password entries (both
109n/a in clear text). Eg:
110n/a
111n/a [distutils]
112n/a index-servers =
113n/a pypi
114n/a
115n/a [pypi]
116n/a username: fred
117n/a password: sekrit
118n/a
119n/a Otherwise, to figure who the user is, we offer the user three
120n/a choices:
121n/a
122n/a 1. use existing login,
123n/a 2. register as a new user, or
124n/a 3. set the password to a random string and email the user.
125n/a
126n/a '''
127n/a # see if we can short-cut and get the username/password from the
128n/a # config
129n/a if self.has_config:
130n/a choice = '1'
131n/a username = self.username
132n/a password = self.password
133n/a else:
134n/a choice = 'x'
135n/a username = password = ''
136n/a
137n/a # get the user's login info
138n/a choices = '1 2 3 4'.split()
139n/a while choice not in choices:
140n/a self.announce('''\
141n/aWe need to know who you are, so please choose either:
142n/a 1. use your existing login,
143n/a 2. register as a new user,
144n/a 3. have the server generate a new password for you (and email it to you), or
145n/a 4. quit
146n/aYour selection [default 1]: ''', log.INFO)
147n/a choice = input()
148n/a if not choice:
149n/a choice = '1'
150n/a elif choice not in choices:
151n/a print('Please choose one of the four options!')
152n/a
153n/a if choice == '1':
154n/a # get the username and password
155n/a while not username:
156n/a username = input('Username: ')
157n/a while not password:
158n/a password = getpass.getpass('Password: ')
159n/a
160n/a # set up the authentication
161n/a auth = urllib.request.HTTPPasswordMgr()
162n/a host = urllib.parse.urlparse(self.repository)[1]
163n/a auth.add_password(self.realm, host, username, password)
164n/a # send the info to the server and report the result
165n/a code, result = self.post_to_server(self.build_post_data('submit'),
166n/a auth)
167n/a self.announce('Server response (%s): %s' % (code, result),
168n/a log.INFO)
169n/a
170n/a # possibly save the login
171n/a if code == 200:
172n/a if self.has_config:
173n/a # sharing the password in the distribution instance
174n/a # so the upload command can reuse it
175n/a self.distribution.password = password
176n/a else:
177n/a self.announce(('I can store your PyPI login so future '
178n/a 'submissions will be faster.'), log.INFO)
179n/a self.announce('(the login will be stored in %s)' % \
180n/a self._get_rc_file(), log.INFO)
181n/a choice = 'X'
182n/a while choice.lower() not in 'yn':
183n/a choice = input('Save your login (y/N)?')
184n/a if not choice:
185n/a choice = 'n'
186n/a if choice.lower() == 'y':
187n/a self._store_pypirc(username, password)
188n/a
189n/a elif choice == '2':
190n/a data = {':action': 'user'}
191n/a data['name'] = data['password'] = data['email'] = ''
192n/a data['confirm'] = None
193n/a while not data['name']:
194n/a data['name'] = input('Username: ')
195n/a while data['password'] != data['confirm']:
196n/a while not data['password']:
197n/a data['password'] = getpass.getpass('Password: ')
198n/a while not data['confirm']:
199n/a data['confirm'] = getpass.getpass(' Confirm: ')
200n/a if data['password'] != data['confirm']:
201n/a data['password'] = ''
202n/a data['confirm'] = None
203n/a print("Password and confirm don't match!")
204n/a while not data['email']:
205n/a data['email'] = input(' EMail: ')
206n/a code, result = self.post_to_server(data)
207n/a if code != 200:
208n/a log.info('Server response (%s): %s', code, result)
209n/a else:
210n/a log.info('You will receive an email shortly.')
211n/a log.info(('Follow the instructions in it to '
212n/a 'complete registration.'))
213n/a elif choice == '3':
214n/a data = {':action': 'password_reset'}
215n/a data['email'] = ''
216n/a while not data['email']:
217n/a data['email'] = input('Your email address: ')
218n/a code, result = self.post_to_server(data)
219n/a log.info('Server response (%s): %s', code, result)
220n/a
221n/a def build_post_data(self, action):
222n/a # figure the data to send - the metadata plus some additional
223n/a # information used by the package server
224n/a meta = self.distribution.metadata
225n/a data = {
226n/a ':action': action,
227n/a 'metadata_version' : '1.0',
228n/a 'name': meta.get_name(),
229n/a 'version': meta.get_version(),
230n/a 'summary': meta.get_description(),
231n/a 'home_page': meta.get_url(),
232n/a 'author': meta.get_contact(),
233n/a 'author_email': meta.get_contact_email(),
234n/a 'license': meta.get_licence(),
235n/a 'description': meta.get_long_description(),
236n/a 'keywords': meta.get_keywords(),
237n/a 'platform': meta.get_platforms(),
238n/a 'classifiers': meta.get_classifiers(),
239n/a 'download_url': meta.get_download_url(),
240n/a # PEP 314
241n/a 'provides': meta.get_provides(),
242n/a 'requires': meta.get_requires(),
243n/a 'obsoletes': meta.get_obsoletes(),
244n/a }
245n/a if data['provides'] or data['requires'] or data['obsoletes']:
246n/a data['metadata_version'] = '1.1'
247n/a return data
248n/a
249n/a def post_to_server(self, data, auth=None):
250n/a ''' Post a query to the server, and return a string response.
251n/a '''
252n/a if 'name' in data:
253n/a self.announce('Registering %s to %s' % (data['name'],
254n/a self.repository),
255n/a log.INFO)
256n/a # Build up the MIME payload for the urllib2 POST data
257n/a boundary = '--------------GHSKFJDLGDS7543FJKLFHRE75642756743254'
258n/a sep_boundary = '\n--' + boundary
259n/a end_boundary = sep_boundary + '--'
260n/a body = io.StringIO()
261n/a for key, value in data.items():
262n/a # handle multiple entries for the same name
263n/a if type(value) not in (type([]), type( () )):
264n/a value = [value]
265n/a for value in value:
266n/a value = str(value)
267n/a body.write(sep_boundary)
268n/a body.write('\nContent-Disposition: form-data; name="%s"'%key)
269n/a body.write("\n\n")
270n/a body.write(value)
271n/a if value and value[-1] == '\r':
272n/a body.write('\n') # write an extra newline (lurve Macs)
273n/a body.write(end_boundary)
274n/a body.write("\n")
275n/a body = body.getvalue().encode("utf-8")
276n/a
277n/a # build the Request
278n/a headers = {
279n/a 'Content-type': 'multipart/form-data; boundary=%s; charset=utf-8'%boundary,
280n/a 'Content-length': str(len(body))
281n/a }
282n/a req = urllib.request.Request(self.repository, body, headers)
283n/a
284n/a # handle HTTP and include the Basic Auth handler
285n/a opener = urllib.request.build_opener(
286n/a urllib.request.HTTPBasicAuthHandler(password_mgr=auth)
287n/a )
288n/a data = ''
289n/a try:
290n/a result = opener.open(req)
291n/a except urllib.error.HTTPError as e:
292n/a if self.show_response:
293n/a data = e.fp.read()
294n/a result = e.code, e.msg
295n/a except urllib.error.URLError as e:
296n/a result = 500, str(e)
297n/a else:
298n/a if self.show_response:
299n/a data = self._read_pypi_response(result)
300n/a result = 200, 'OK'
301n/a if self.show_response:
302n/a msg = '\n'.join(('-' * 75, data, '-' * 75))
303n/a self.announce(msg, log.INFO)
304n/a return result