mirror of
https://github.com/python/cpython.git
synced 2025-07-24 11:44:31 +00:00
initial import of the packaging package in the standard library
This commit is contained in:
parent
566f8a646e
commit
1231a4e097
193 changed files with 30376 additions and 149 deletions
99
Lib/packaging/pypi/wrapper.py
Normal file
99
Lib/packaging/pypi/wrapper.py
Normal file
|
@ -0,0 +1,99 @@
|
|||
"""Convenient client for all PyPI APIs.
|
||||
|
||||
This module provides a ClientWrapper class which will use the "simple"
|
||||
or XML-RPC API to request information or files from an index.
|
||||
"""
|
||||
|
||||
from packaging.pypi import simple, xmlrpc
|
||||
|
||||
_WRAPPER_MAPPINGS = {'get_release': 'simple',
|
||||
'get_releases': 'simple',
|
||||
'search_projects': 'simple',
|
||||
'get_metadata': 'xmlrpc',
|
||||
'get_distributions': 'simple'}
|
||||
|
||||
_WRAPPER_INDEXES = {'xmlrpc': xmlrpc.Client,
|
||||
'simple': simple.Crawler}
|
||||
|
||||
|
||||
def switch_index_if_fails(func, wrapper):
|
||||
"""Decorator that switch of index (for instance from xmlrpc to simple)
|
||||
if the first mirror return an empty list or raises an exception.
|
||||
"""
|
||||
def decorator(*args, **kwargs):
|
||||
retry = True
|
||||
exception = None
|
||||
methods = [func]
|
||||
for f in wrapper._indexes.values():
|
||||
if f != func.__self__ and hasattr(f, func.__name__):
|
||||
methods.append(getattr(f, func.__name__))
|
||||
for method in methods:
|
||||
try:
|
||||
response = method(*args, **kwargs)
|
||||
retry = False
|
||||
except Exception as e:
|
||||
exception = e
|
||||
if not retry:
|
||||
break
|
||||
if retry and exception:
|
||||
raise exception
|
||||
else:
|
||||
return response
|
||||
return decorator
|
||||
|
||||
|
||||
class ClientWrapper:
|
||||
"""Wrapper around simple and xmlrpc clients,
|
||||
|
||||
Choose the best implementation to use depending the needs, using the given
|
||||
mappings.
|
||||
If one of the indexes returns an error, tries to use others indexes.
|
||||
|
||||
:param index: tell which index to rely on by default.
|
||||
:param index_classes: a dict of name:class to use as indexes.
|
||||
:param indexes: a dict of name:index already instantiated
|
||||
:param mappings: the mappings to use for this wrapper
|
||||
"""
|
||||
|
||||
def __init__(self, default_index='simple', index_classes=_WRAPPER_INDEXES,
|
||||
indexes={}, mappings=_WRAPPER_MAPPINGS):
|
||||
self._projects = {}
|
||||
self._mappings = mappings
|
||||
self._indexes = indexes
|
||||
self._default_index = default_index
|
||||
|
||||
# instantiate the classes and set their _project attribute to the one
|
||||
# of the wrapper.
|
||||
for name, cls in index_classes.items():
|
||||
obj = self._indexes.setdefault(name, cls())
|
||||
obj._projects = self._projects
|
||||
obj._index = self
|
||||
|
||||
def __getattr__(self, method_name):
|
||||
"""When asking for methods of the wrapper, return the implementation of
|
||||
the wrapped classes, depending the mapping.
|
||||
|
||||
Decorate the methods to switch of implementation if an error occurs
|
||||
"""
|
||||
real_method = None
|
||||
if method_name in _WRAPPER_MAPPINGS:
|
||||
obj = self._indexes[_WRAPPER_MAPPINGS[method_name]]
|
||||
real_method = getattr(obj, method_name)
|
||||
else:
|
||||
# the method is not defined in the mappings, so we try first to get
|
||||
# it via the default index, and rely on others if needed.
|
||||
try:
|
||||
real_method = getattr(self._indexes[self._default_index],
|
||||
method_name)
|
||||
except AttributeError:
|
||||
other_indexes = [i for i in self._indexes
|
||||
if i != self._default_index]
|
||||
for index in other_indexes:
|
||||
real_method = getattr(self._indexes[index], method_name,
|
||||
None)
|
||||
if real_method:
|
||||
break
|
||||
if real_method:
|
||||
return switch_index_if_fails(real_method, self)
|
||||
else:
|
||||
raise AttributeError("No index have attribute '%s'" % method_name)
|
Loading…
Add table
Add a link
Reference in a new issue