Source code for arx.sources.s3

import boto3
from sh import Command, chmod, cp, mkdir, rsync, ErrorReturnCode
import uritools

from ..decorators import schemes
from ..err import Err
from .files import File, FileTar
from .http import HTTP, HTTPTar, HTTPJar
from .jar import Jar
from .core import onepath, oneurl, SignableURL, twopaths
from .tar import Tar


[docs]class S3(SignableURL): """Links objects in S3. The URL can end with a ``/`` to give it directory nature; otherwise it has file nature. With directory nature, the directory is unpacked recursively. """ @oneurl @schemes('s3') def __init__(self, url): self.url = url if url.fragment is not None: raise Invalid('Arx can not work with plain S3 URLs that have ' 'fragments.') @property def dirlike(self): return self.url.path.endswith('/') @property def base(self): # Allows subclasses to inherit this implementation by throwing away the # prefix. scheme = self.url.scheme.split('+')[-1] return self.url._replace(scheme=scheme, fragment=None) def signed_get(self, seconds=3600): if self.dirlike: raise Invalid('Not able to sign directory-like S3 URLs.') data = dict(Bucket=self.url.host, Key=self.url.path) s3 = boto3.client('s3') link = s3.generate_presigned_url('get_object', data, ExpiresIn=seconds) return link def sign(self): return HTTP(self.signed_get()) @onepath def cache(self, cache): data = self.dataname(cache) cmd = Command('aws') sub = 'sync' if self.dirlike else 'cp' cmd('s3', sub, uritools.uriunsplit(self.base), str(data)) return File('file:///' + str(data)) @twopaths def place(self, cache, path): mkdir('-p', path.dirname) if self.dirlike: rsync('-ai', str(self.data(cache)) + '/', str(path)) else: cp(str(self.data(cache)), str(path)) @onepath def run(self, cache, args=[]): if self.dirlike: raise Invalid('Arx can not run directory-like (ending with `/`) ' 'S3 paths.') item = self.data(cache) chmod('a+rx', str(item)) cmd = Command(str(item)) cmd(*args)
[docs]class S3Tar(Tar, S3): """Links to tar archives available over S3. Note that these URLs may not end with a slash. These URLs have directory nature unless a fragment is passed, as described under :class:`~arx.sources.tar.Tar`. """ @oneurl @schemes('tar+s3') def __init__(self, url): self.url = url if self.dirlike: raise Invalid('Arx can not treat directory-like (ending with `/`) ' 'S3 paths like tarballs.') @onepath def cache(self, cache): as_file = super(S3Tar, self).cache(cache) return FileTar.resolve(as_file.resolved) def sign(self): return HTTPTar('tar+' + self.signed_get())
class S3Jar(Jar, S3): @oneurl @schemes('jar+s3') def __init__(self, url): self.url = url if self.url.fragment is not None: raise Invalid('Arx can not handle Jar S3 URLs with fragments.') if self.dirlike: raise Invalid('Arx can not treat directory-like (ending with `/`) ' 'S3 paths like Jars.') def sign(self): return HTTPJar('jar+' + self.signed_get()) class Invalid(Err): pass def no_credentials(): aws = Command('aws') try: aws('sts', 'get-caller-identity') return False except ErrorReturnCode: return True