"""
"""
from __future__ import absolute_import
import subprocess
from os import makedirs, listdir
from os.path import exists, join, basename
from abc import ABCMeta, abstractmethod
try:
from urllib.parse import urlparse
except ImportError:
from urlparse import urlparse
import requests
import yaml
from pkg_resources import parse_version
from six import with_metaclass
from .io import warn, info, debug, fatal
[docs]class BaseCacher(with_metaclass(ABCMeta, object)):
def __init__(self, cache_path):
if not exists(cache_path):
makedirs(cache_path)
self.cache_path = cache_path
[docs] def abspath(self, name):
return join(self.cache_path, name)
[docs] @abstractmethod
def check(self, name, **kwargs):
"""
"""
[docs] @abstractmethod
def cache(self, name, **kwargs):
"""
"""
[docs]class TarballCacher(BaseCacher):
def __init__(self, cache_path):
cache_path = join(cache_path, 'tarballs')
super(TarballCacher, self).__init__(cache_path)
[docs] def check(self, name, version=None):
if version is None:
cfpath = self.abspath(name)
if exists(cfpath):
return cfpath
else:
name = name.lower()
for cfile in listdir(self.cache_path):
if cfile.lower().startswith(name + '-'):
cver = cfile[len(name + '-'):]
if cver.endswith('.tar.gz'):
ext = len('.tar.gz')
elif cver.endswith('.tgz'):
ext = len('.tgz')
elif cver.endswith('.tar.bz2'):
ext = len('.tar.bz2')
elif cver.endswith('.zip'):
ext = len('.zip')
else:
warn('Unknown extension on cached file: %s', cfile)
continue
cver = cver[:-ext]
if parse_version(cver) == parse_version(version):
return self.abspath(cfile)
return None
[docs]class UrlCacher(TarballCacher):
[docs] def check(self, name, **kwargs):
tgz = basename(urlparse(name).path)
return super(UrlCacher, self).check(tgz)
[docs] def cache(self, name, **kwargs):
cfpath = self.check(name)
tgz = basename(urlparse(name).path)
if cfpath is not None:
info('Using cached file: %s', cfpath)
else:
cfpath = self.abspath(tgz)
r = requests.get(name)
with open(cfpath, 'wb') as handle:
for chunk in r.iter_content(chunk_size=1024):
handle.write(chunk)
[docs]class PipSourceCacher(TarballCacher):
[docs] def cache(self, name, version=None, fail_ok=False, **kwargs):
if version is None:
fatal('A version must be provided when caching from pip')
cfpath = self.check(name, version=version)
if cfpath is not None:
info('Using cached sdist: %s', cfpath)
else:
try:
# TODO: use the pip API
cmd = [
'pip', '--no-cache-dir', 'install', '-d', self.cache_path,
'--no-binary', ':all:', '--no-deps', name + '==' + version
]
info('Fetching sdist: %s', name)
debug('Executing: %s', ' '.join(cmd))
subprocess.check_call(cmd)
except subprocess.CalledProcessError:
if not fail_ok:
raise
[docs]class CacheManager(object):
def __init__(self, cache_path):
self.cache_path = cache_path
self.cachers = {}
self.load_cachers()
[docs] def load_cachers(self):
self.cachers['pip'] = PipSourceCacher(self.cache_path)
self.cachers['url'] = UrlCacher(self.cache_path)
self.cachers['platform'] = PlatformStringCacher(self.cache_path)
[docs] def pip_check(self, name, version):
return self.cachers['pip'].check(name, version=version)
[docs] def url_check(self, name):
return self.cachers['url'].check(name)
[docs] def pip_cache(self, name, version, fail_ok=False):
return self.cachers['pip'].cache(
name,
version=version,
fail_ok=fail_ok)
[docs] def url_cache(self, name):
return self.cachers['url'].cache(name)