Skip to content

Commit a526121

Browse files
author
shariqiqbal2810
committed
Modified tests for sustainability
- S3DataGrabber uses anon connection to grab from openfmri bucket - S3DataSink uses fakes3 server to test functionality - Added 'gem install fakes3' to circle.yml
1 parent e3e4d3d commit a526121

File tree

3 files changed

+71
-34
lines changed

3 files changed

+71
-34
lines changed

circle.yml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,8 @@ dependencies:
99
override:
1010
- pip install --upgrade pip
1111
- pip install -e .
12-
- pip install matplotlib sphinx ipython boto3
12+
- pip install matplotlib sphinx ipython boto
13+
- gem install fakes3
1314
- if [[ ! -d ~/examples/data ]]; then wget "http://tcpdiag.dl.sourceforge.net/project/nipy/nipype/nipype-0.2/nipype-tutorial.tar.bz2"; tar jxvf nipype-tutorial.tar.bz2; mkdir ~/examples; mv nipype-tutorial/* ~/examples/; fi
1415
# we download this manually because CircleCI does not cache apt
1516
- if [[ ! -d ~/examples/feeds ]]; then wget "http://fsl.fmrib.ox.ac.uk/fsldownloads/fsl-5.0.8-feeds.tar.gz"; tar zxvf fsl-5.0.8-feeds.tar.gz; mv feeds ~/examples/; fi

nipype/interfaces/io.py

Lines changed: 34 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,8 @@
4646
pass
4747

4848
try:
49-
import boto3
49+
import boto
50+
from boto.s3.connection import S3Connection, OrdinaryCallingFormat
5051
except:
5152
pass
5253

@@ -377,13 +378,16 @@ def _list_outputs(self):
377378

378379

379380
class S3DataSinkInputSpec(DynamicTraitedSpec, BaseInterfaceInputSpec):
381+
testing = traits.Bool(False, usedefault=True,
382+
desc='Flag for using local fakes3 server.'
383+
' (for testing purposes only)')
384+
anon = traits.Bool(False, usedefault=True,
385+
desc='Use anonymous connection to s3')
380386
bucket = traits.Str(mandatory=True,
381387
desc='Amazon S3 bucket where your data is stored')
382388
bucket_path = traits.Str('', usedefault=True,
383389
desc='Location within your bucket to store '
384390
'data.')
385-
region = traits.Str('us-east-1', usedefault=True,
386-
desc='Region of s3 bucket')
387391
base_directory = Directory(
388392
desc='Path to the base directory for storing data.')
389393
container = traits.Str(
@@ -435,8 +439,14 @@ def _list_outputs(self):
435439
return outputs
436440

437441
def localtos3(self, paths):
438-
client = boto3.client('s3', self.inputs.region)
439-
transfer = boto3.s3.transfer.S3Transfer(client)
442+
if self.inputs.testing:
443+
conn = S3Connection(anon=True, is_secure=False, port=4567,
444+
host='localhost',
445+
calling_format=OrdinaryCallingFormat())
446+
447+
else:
448+
conn = S3Connection(anon=self.inputs.anon)
449+
bkt = conn.get_bucket(self.inputs.bucket)
440450
s3paths = []
441451

442452
for path in paths:
@@ -446,17 +456,23 @@ def localtos3(self, paths):
446456
s3path = path[bd_index+len(self.inputs.base_directory):] # cut out base directory
447457
if s3path[0] == os.path.sep:
448458
s3path = s3path[1:]
449-
else: # base_directory isn't in path, simply place all files in bucket_path folder
450-
s3path = os.path.split(path)[1] # take filename from path
451-
s3path = os.path.join(self.inputs.bucket_path, path)
459+
else: # base_directory isn't in path, simply place all files in bucket_path folder
460+
s3path = os.path.split(path)[1] # take filename from path
461+
s3path = os.path.join(self.inputs.bucket_path, s3path)
462+
if s3path[-1] == os.path.sep:
463+
s3path = s3path[:-1]
452464
s3paths.append(s3path)
453465

454-
transfer.upload_file(path, self.inputs.bucket, s3path)
466+
k = boto.s3.key.Key(bkt)
467+
k.key = s3path
468+
k.set_contents_from_filename(path)
455469

456470
return s3paths
457471

458472

459473
class S3DataGrabberInputSpec(DynamicTraitedSpec, BaseInterfaceInputSpec):
474+
anon = traits.Bool(False, usedefault=True,
475+
desc='Use anonymous connection to s3')
460476
region = traits.Str('us-east-1', usedefault=True,
461477
desc='Region of s3 bucket')
462478
bucket = traits.Str(mandatory=True,
@@ -557,9 +573,9 @@ def _list_outputs(self):
557573

558574
outputs = {}
559575
# get list of all files in s3 bucket
560-
s3 = boto3.resource('s3')
561-
bkt = s3.Bucket(self.inputs.bucket)
562-
bkt_files = list(k.key for k in bkt.objects.all())
576+
conn = boto.connect_s3(anon=self.inputs.anon)
577+
bkt = conn.get_bucket(self.inputs.bucket)
578+
bkt_files = list(k.key for k in bkt.list())
563579

564580
# keys are outfields, args are template args for the outfield
565581
for key, args in self.inputs.template_args.items():
@@ -642,15 +658,15 @@ def _list_outputs(self):
642658
paths = outputs[key]
643659
for i in range(len(paths)):
644660
path = paths[i]
645-
outputs[key][i] = self.s3tolocal(path)
661+
outputs[key][i] = self.s3tolocal(path, bkt)
646662
elif type(outputs[key]) == str:
647-
outputs[key] = self.s3tolocal(outputs[key])
663+
outputs[key] = self.s3tolocal(outputs[key], bkt)
648664

649665
return outputs
650666

651667
# Takes an s3 address and downloads the file to a local
652668
# directory, returning the local path.
653-
def s3tolocal(self, s3path):
669+
def s3tolocal(self, s3path, bkt):
654670
# path formatting
655671
if not os.path.split(self.inputs.local_directory)[1] == '':
656672
self.inputs.local_directory += '/'
@@ -663,9 +679,9 @@ def s3tolocal(self, s3path):
663679
localdir = os.path.split(localpath)[0]
664680
if not os.path.exists(localdir):
665681
os.makedirs(localdir)
666-
client = boto3.client('s3', self.inputs.region)
667-
transfer = boto3.s3.transfer.S3Transfer(client)
668-
transfer.download_file(self.inputs.bucket, s3path, localpath)
682+
k = boto.s3.key.Key(bkt)
683+
k.key = s3path
684+
k.get_contents_to_filename(localpath)
669685
return localpath
670686

671687

nipype/interfaces/tests/test_io.py

Lines changed: 35 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
import shutil
66
import os.path as op
77
from tempfile import mkstemp, mkdtemp
8+
from subprocess import Popen
89

910
from nose.tools import assert_raises
1011
import nipype
@@ -14,7 +15,8 @@
1415

1516
noboto = False
1617
try:
17-
import boto3
18+
import boto
19+
from boto.s3.connection import S3Connection, OrdinaryCallingFormat
1820
except:
1921
noboto = True
2022

@@ -88,6 +90,7 @@ def test_selectfiles_valueerror():
8890
@skipif(noboto)
8991
def test_s3datagrabber_communication():
9092
dg = nio.S3DataGrabber(infields=['subj_id', 'run_num'], outfields=['func', 'struct'])
93+
dg.inputs.anon = True
9194
dg.inputs.bucket = 'openfmri'
9295
dg.inputs.bucket_path = 'ds001/'
9396
tempdir = mkdtemp()
@@ -202,8 +205,19 @@ def test_s3datasink_substitutions():
202205
f = os.path.join(indir, n)
203206
files.append(f)
204207
open(f, 'w')
208+
209+
# run fakes3 server and set up bucket
210+
fakes3dir = op.expanduser('~/fakes3')
211+
proc = Popen(['fakes3', '-r', fakes3dir, '-p', '4567'], stdout=open(os.devnull, 'wb'))
212+
conn = S3Connection(anon=True, is_secure=False, port=4567,
213+
host='localhost',
214+
calling_format=OrdinaryCallingFormat())
215+
conn.create_bucket('test')
216+
205217
ds = nio.S3DataSink(
206-
bucket='nipype-test',
218+
testing=True,
219+
anon=True,
220+
bucket='test',
207221
bucket_path='output/',
208222
parametrization=False,
209223
base_directory=outdir,
@@ -222,27 +236,33 @@ def test_s3datasink_substitutions():
222236
x in glob.glob(os.path.join(outdir, '*'))]), \
223237
['!-yz-b.n', 'ABABAB.n'] # so we got re used 2nd and both patterns
224238

225-
s3 = boto3.resource('s3')
226-
bkt = s3.Bucket(ds.inputs.bucket)
227-
bkt_files = list(k.key for k in bkt.objects.all())
239+
bkt = conn.get_bucket(ds.inputs.bucket)
240+
bkt_files = list(k for k in bkt.list())
228241

229242
found = [False, False]
230-
del_dict = {'Objects':[],'Quiet': True}
231-
for key in bkt_files:
232-
if '!-yz-b.n' in key:
243+
failed_deletes = 0
244+
for k in bkt_files:
245+
if '!-yz-b.n' in k.key:
233246
found[0] = True
234-
del_dict["Objects"].append({'Key': key})
235-
if 'ABABAB.n' in key:
247+
try:
248+
bkt.delete_key(k)
249+
except:
250+
failed_deletes += 1
251+
elif 'ABABAB.n' in k.key:
236252
found[1] = True
237-
del_dict["Objects"].append({'Key': key})
253+
try:
254+
bkt.delete_key(k)
255+
except:
256+
failed_deletes += 1
257+
258+
# ensure delete requests were successful
259+
yield assert_equal, failed_deletes, 0
238260

239261
# ensure both keys are found in bucket
240262
yield assert_equal, found.count(True), 2
241263

242-
resp = bkt.delete_objects(Delete=del_dict, RequestPayer='requester')
243-
# ensure delete request was successful
244-
yield assert_equal, resp["ResponseMetadata"]["HTTPStatusCode"], 200
245-
264+
proc.kill()
265+
shutil.rmtree(fakes3dir)
246266
shutil.rmtree(indir)
247267
shutil.rmtree(outdir)
248268

0 commit comments

Comments
 (0)