ยปCore Development>Code coverage>Lib/packaging/pypi/xmlrpc.py

Python code coverage for Lib/packaging/pypi/xmlrpc.py

#countcontent
1n/a"""Spider using the XML-RPC PyPI API.
2n/a
3n/aThis module contains the class Client, a spider that can be used to find
4n/aand retrieve distributions from a project index (like the Python Package
5n/aIndex), using its XML-RPC API (see documentation of the reference
6n/aimplementation at http://wiki.python.org/moin/PyPiXmlRpc).
7n/a"""
8n/a
9n/aimport xmlrpc.client
10n/a
11n/afrom packaging import logger
12n/afrom packaging.errors import IrrationalVersionError
13n/afrom packaging.version import get_version_predicate
14n/afrom packaging.pypi.base import BaseClient
15n/afrom packaging.pypi.errors import (ProjectNotFound, InvalidSearchField,
16n/a ReleaseNotFound)
17n/afrom packaging.pypi.dist import ReleaseInfo
18n/a
19n/a__all__ = ['Client', 'DEFAULT_XMLRPC_INDEX_URL']
20n/a
21n/aDEFAULT_XMLRPC_INDEX_URL = 'http://python.org/pypi'
22n/a
23n/a_SEARCH_FIELDS = ['name', 'version', 'author', 'author_email', 'maintainer',
24n/a 'maintainer_email', 'home_page', 'license', 'summary',
25n/a 'description', 'keywords', 'platform', 'download_url']
26n/a
27n/a
28n/aclass Client(BaseClient):
29n/a """Client to query indexes using XML-RPC method calls.
30n/a
31n/a If no server_url is specified, use the default PyPI XML-RPC URL,
32n/a defined in the DEFAULT_XMLRPC_INDEX_URL constant::
33n/a
34n/a >>> client = Client()
35n/a >>> client.server_url == DEFAULT_XMLRPC_INDEX_URL
36n/a True
37n/a
38n/a >>> client = Client("http://someurl/")
39n/a >>> client.server_url
40n/a 'http://someurl/'
41n/a """
42n/a
43n/a def __init__(self, server_url=DEFAULT_XMLRPC_INDEX_URL, prefer_final=False,
44n/a prefer_source=True):
45n/a super(Client, self).__init__(prefer_final, prefer_source)
46n/a self.server_url = server_url
47n/a self._projects = {}
48n/a
49n/a def get_release(self, requirements, prefer_final=False):
50n/a """Return a release with all complete metadata and distribution
51n/a related informations.
52n/a """
53n/a prefer_final = self._get_prefer_final(prefer_final)
54n/a predicate = get_version_predicate(requirements)
55n/a releases = self.get_releases(predicate.name)
56n/a release = releases.get_last(predicate, prefer_final)
57n/a self.get_metadata(release.name, str(release.version))
58n/a self.get_distributions(release.name, str(release.version))
59n/a return release
60n/a
61n/a def get_releases(self, requirements, prefer_final=None, show_hidden=True,
62n/a force_update=False):
63n/a """Return the list of existing releases for a specific project.
64n/a
65n/a Cache the results from one call to another.
66n/a
67n/a If show_hidden is True, return the hidden releases too.
68n/a If force_update is True, reprocess the index to update the
69n/a informations (eg. make a new XML-RPC call).
70n/a ::
71n/a
72n/a >>> client = Client()
73n/a >>> client.get_releases('Foo')
74n/a ['1.1', '1.2', '1.3']
75n/a
76n/a If no such project exists, raise a ProjectNotFound exception::
77n/a
78n/a >>> client.get_project_versions('UnexistingProject')
79n/a ProjectNotFound: UnexistingProject
80n/a
81n/a """
82n/a def get_versions(project_name, show_hidden):
83n/a return self.proxy.package_releases(project_name, show_hidden)
84n/a
85n/a predicate = get_version_predicate(requirements)
86n/a prefer_final = self._get_prefer_final(prefer_final)
87n/a project_name = predicate.name
88n/a if not force_update and (project_name.lower() in self._projects):
89n/a project = self._projects[project_name.lower()]
90n/a if not project.contains_hidden and show_hidden:
91n/a # if hidden releases are requested, and have an existing
92n/a # list of releases that does not contains hidden ones
93n/a all_versions = get_versions(project_name, show_hidden)
94n/a existing_versions = project.get_versions()
95n/a hidden_versions = set(all_versions) - set(existing_versions)
96n/a for version in hidden_versions:
97n/a project.add_release(release=ReleaseInfo(project_name,
98n/a version, index=self._index))
99n/a else:
100n/a versions = get_versions(project_name, show_hidden)
101n/a if not versions:
102n/a raise ProjectNotFound(project_name)
103n/a project = self._get_project(project_name)
104n/a project.add_releases([ReleaseInfo(project_name, version,
105n/a index=self._index)
106n/a for version in versions])
107n/a project = project.filter(predicate)
108n/a if len(project) == 0:
109n/a raise ReleaseNotFound("%s" % predicate)
110n/a project.sort_releases(prefer_final)
111n/a return project
112n/a
113n/a
114n/a def get_distributions(self, project_name, version):
115n/a """Grab informations about distributions from XML-RPC.
116n/a
117n/a Return a ReleaseInfo object, with distribution-related informations
118n/a filled in.
119n/a """
120n/a url_infos = self.proxy.release_urls(project_name, version)
121n/a project = self._get_project(project_name)
122n/a if version not in project.get_versions():
123n/a project.add_release(release=ReleaseInfo(project_name, version,
124n/a index=self._index))
125n/a release = project.get_release(version)
126n/a for info in url_infos:
127n/a packagetype = info['packagetype']
128n/a dist_infos = {'url': info['url'],
129n/a 'hashval': info['md5_digest'],
130n/a 'hashname': 'md5',
131n/a 'is_external': False,
132n/a 'python_version': info['python_version']}
133n/a release.add_distribution(packagetype, **dist_infos)
134n/a return release
135n/a
136n/a def get_metadata(self, project_name, version):
137n/a """Retrieve project metadata.
138n/a
139n/a Return a ReleaseInfo object, with metadata informations filled in.
140n/a """
141n/a # to be case-insensitive, get the informations from the XMLRPC API
142n/a projects = [d['name'] for d in
143n/a self.proxy.search({'name': project_name})
144n/a if d['name'].lower() == project_name]
145n/a if len(projects) > 0:
146n/a project_name = projects[0]
147n/a
148n/a metadata = self.proxy.release_data(project_name, version)
149n/a project = self._get_project(project_name)
150n/a if version not in project.get_versions():
151n/a project.add_release(release=ReleaseInfo(project_name, version,
152n/a index=self._index))
153n/a release = project.get_release(version)
154n/a release.set_metadata(metadata)
155n/a return release
156n/a
157n/a def search_projects(self, name=None, operator="or", **kwargs):
158n/a """Find using the keys provided in kwargs.
159n/a
160n/a You can set operator to "and" or "or".
161n/a """
162n/a for key in kwargs:
163n/a if key not in _SEARCH_FIELDS:
164n/a raise InvalidSearchField(key)
165n/a if name:
166n/a kwargs["name"] = name
167n/a projects = self.proxy.search(kwargs, operator)
168n/a for p in projects:
169n/a project = self._get_project(p['name'])
170n/a try:
171n/a project.add_release(release=ReleaseInfo(p['name'],
172n/a p['version'], metadata={'summary': p['summary']},
173n/a index=self._index))
174n/a except IrrationalVersionError as e:
175n/a logger.warning("Irrational version error found: %s", e)
176n/a return [self._projects[p['name'].lower()] for p in projects]
177n/a
178n/a def get_all_projects(self):
179n/a """Return the list of all projects registered in the package index"""
180n/a projects = self.proxy.list_packages()
181n/a for name in projects:
182n/a self.get_releases(name, show_hidden=True)
183n/a
184n/a return [self._projects[name.lower()] for name in set(projects)]
185n/a
186n/a @property
187n/a def proxy(self):
188n/a """Property used to return the XMLRPC server proxy.
189n/a
190n/a If no server proxy is defined yet, creates a new one::
191n/a
192n/a >>> client = Client()
193n/a >>> client.proxy()
194n/a <ServerProxy for python.org/pypi>
195n/a
196n/a """
197n/a if not hasattr(self, '_server_proxy'):
198n/a self._server_proxy = xmlrpc.client.ServerProxy(self.server_url)
199n/a
200n/a return self._server_proxy