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

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

#countcontent
1n/a"""Convenient client for all PyPI APIs.
2n/a
3n/aThis module provides a ClientWrapper class which will use the "simple"
4n/aor XML-RPC API to request information or files from an index.
5n/a"""
6n/a
7n/afrom packaging.pypi import simple, xmlrpc
8n/a
9n/a_WRAPPER_MAPPINGS = {'get_release': 'simple',
10n/a 'get_releases': 'simple',
11n/a 'search_projects': 'simple',
12n/a 'get_metadata': 'xmlrpc',
13n/a 'get_distributions': 'simple'}
14n/a
15n/a_WRAPPER_INDEXES = {'xmlrpc': xmlrpc.Client,
16n/a 'simple': simple.Crawler}
17n/a
18n/a
19n/adef switch_index_if_fails(func, wrapper):
20n/a """Decorator that switch of index (for instance from xmlrpc to simple)
21n/a if the first mirror return an empty list or raises an exception.
22n/a """
23n/a def decorator(*args, **kwargs):
24n/a retry = True
25n/a exception = None
26n/a methods = [func]
27n/a for f in wrapper._indexes.values():
28n/a if f != func.__self__ and hasattr(f, func.__name__):
29n/a methods.append(getattr(f, func.__name__))
30n/a for method in methods:
31n/a try:
32n/a response = method(*args, **kwargs)
33n/a retry = False
34n/a except Exception as e:
35n/a exception = e
36n/a if not retry:
37n/a break
38n/a if retry and exception:
39n/a raise exception
40n/a else:
41n/a return response
42n/a return decorator
43n/a
44n/a
45n/aclass ClientWrapper:
46n/a """Wrapper around simple and xmlrpc clients,
47n/a
48n/a Choose the best implementation to use depending the needs, using the given
49n/a mappings.
50n/a If one of the indexes returns an error, tries to use others indexes.
51n/a
52n/a :param index: tell which index to rely on by default.
53n/a :param index_classes: a dict of name:class to use as indexes.
54n/a :param indexes: a dict of name:index already instantiated
55n/a :param mappings: the mappings to use for this wrapper
56n/a """
57n/a
58n/a def __init__(self, default_index='simple', index_classes=_WRAPPER_INDEXES,
59n/a indexes={}, mappings=_WRAPPER_MAPPINGS):
60n/a self._projects = {}
61n/a self._mappings = mappings
62n/a self._indexes = indexes
63n/a self._default_index = default_index
64n/a
65n/a # instantiate the classes and set their _project attribute to the one
66n/a # of the wrapper.
67n/a for name, cls in index_classes.items():
68n/a obj = self._indexes.setdefault(name, cls())
69n/a obj._projects = self._projects
70n/a obj._index = self
71n/a
72n/a def __getattr__(self, method_name):
73n/a """When asking for methods of the wrapper, return the implementation of
74n/a the wrapped classes, depending the mapping.
75n/a
76n/a Decorate the methods to switch of implementation if an error occurs
77n/a """
78n/a real_method = None
79n/a if method_name in _WRAPPER_MAPPINGS:
80n/a obj = self._indexes[_WRAPPER_MAPPINGS[method_name]]
81n/a real_method = getattr(obj, method_name)
82n/a else:
83n/a # the method is not defined in the mappings, so we try first to get
84n/a # it via the default index, and rely on others if needed.
85n/a try:
86n/a real_method = getattr(self._indexes[self._default_index],
87n/a method_name)
88n/a except AttributeError:
89n/a other_indexes = [i for i in self._indexes
90n/a if i != self._default_index]
91n/a for index in other_indexes:
92n/a real_method = getattr(self._indexes[index], method_name,
93n/a None)
94n/a if real_method:
95n/a break
96n/a if real_method:
97n/a return switch_index_if_fails(real_method, self)
98n/a else:
99n/a raise AttributeError("No index have attribute '%s'" % method_name)