Skip to content

Commit fa864aa

Browse files
authored
Merge pull request #2626 from anibalsolon/enh/crash_file_versioning
ENH: Add versioning metadata to crash files
2 parents eede943 + d3de9f4 commit fa864aa

File tree

3 files changed

+101
-10
lines changed

3 files changed

+101
-10
lines changed

nipype/pipeline/plugins/tools.py

+2-1
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,8 @@ def report_crash(node, traceback=None, hostname=None):
5858
if crashfile.endswith('.txt'):
5959
crash2txt(crashfile, dict(node=node, traceback=traceback))
6060
else:
61-
savepkl(crashfile, dict(node=node, traceback=traceback))
61+
savepkl(crashfile, dict(node=node, traceback=traceback),
62+
versioning=True)
6263
return crashfile
6364

6465

nipype/utils/filemanip.py

+49-8
Original file line numberDiff line numberDiff line change
@@ -628,13 +628,13 @@ def load_json(filename):
628628

629629

630630
def loadcrash(infile, *args):
631-
if '.pkl' in infile:
632-
return loadpkl(infile)
631+
if infile.endswith('pkl') or infile.endswith('pklz'):
632+
return loadpkl(infile, versioning=True)
633633
else:
634634
raise ValueError('Only pickled crashfiles are supported')
635635

636636

637-
def loadpkl(infile):
637+
def loadpkl(infile, versioning=False):
638638
"""Load a zipped or plain cPickled file
639639
"""
640640
fmlogger.debug('Loading pkl: %s', infile)
@@ -643,11 +643,44 @@ def loadpkl(infile):
643643
else:
644644
pkl_file = open(infile, 'rb')
645645

646+
if versioning:
647+
pkl_metadata = {}
648+
649+
# Look if pkl file contains version file
650+
try:
651+
pkl_metadata_line = pkl_file.readline()
652+
pkl_metadata = json.loads(pkl_metadata_line)
653+
except:
654+
# Could not get version info
655+
pkl_file.seek(0)
656+
646657
try:
647-
unpkl = pickle.load(pkl_file)
648-
except UnicodeDecodeError:
649-
unpkl = pickle.load(pkl_file, fix_imports=True, encoding='utf-8')
650-
return unpkl
658+
try:
659+
unpkl = pickle.load(pkl_file)
660+
except UnicodeDecodeError:
661+
unpkl = pickle.load(pkl_file, fix_imports=True, encoding='utf-8')
662+
663+
return unpkl
664+
665+
# Unpickling problems
666+
except Exception as e:
667+
if not versioning:
668+
raise e
669+
670+
from nipype import __version__ as version
671+
672+
if 'version' in pkl_metadata:
673+
if pkl_metadata['version'] != version:
674+
fmlogger.error('Your Nipype version is: %s',
675+
version)
676+
fmlogger.error('Nipype version of the pkl is: %s',
677+
pkl_metadata['version'])
678+
else:
679+
fmlogger.error('No metadata was found in the pkl file.')
680+
fmlogger.error('Make sure that you are using the same Nipype'
681+
'version from the generated pkl.')
682+
683+
raise e
651684

652685

653686
def crash2txt(filename, record):
@@ -682,11 +715,19 @@ def read_stream(stream, logger=None, encoding=None):
682715
return out.splitlines()
683716

684717

685-
def savepkl(filename, record):
718+
def savepkl(filename, record, versioning=False):
686719
if filename.endswith('pklz'):
687720
pkl_file = gzip.open(filename, 'wb')
688721
else:
689722
pkl_file = open(filename, 'wb')
723+
724+
if versioning:
725+
from nipype import __version__ as version
726+
metadata = json.dumps({'version': version})
727+
728+
pkl_file.write(metadata.encode('utf-8'))
729+
pkl_file.write('\n'.encode('utf-8'))
730+
690731
pickle.dump(record, pkl_file)
691732
pkl_file.close()
692733

nipype/utils/tests/test_filemanip.py

+50-1
Original file line numberDiff line numberDiff line change
@@ -8,13 +8,15 @@
88
import time
99
import warnings
1010

11+
import mock
1112
import pytest
1213
from ...testing import TempFATFS
1314
from ...utils.filemanip import (
1415
save_json, load_json, fname_presuffix, fnames_presuffix, hash_rename,
1516
check_forhash, _parse_mount_table, _cifs_table, on_cifs, copyfile,
1617
copyfiles, ensure_list, simplify_list, check_depends,
17-
split_filename, get_related_files, indirectory)
18+
split_filename, get_related_files, indirectory,
19+
loadpkl, loadcrash, savepkl)
1820

1921

2022
def _ignore_atime(stat):
@@ -521,3 +523,50 @@ def test_indirectory(tmpdir):
521523
except ValueError:
522524
pass
523525
assert os.getcwd() == tmpdir.strpath
526+
527+
528+
def test_pklization(tmpdir):
529+
tmpdir.chdir()
530+
531+
exc = Exception("There is something wrong here")
532+
savepkl('./except.pkz', exc)
533+
newexc = loadpkl('./except.pkz')
534+
535+
assert exc.args == newexc.args
536+
assert os.getcwd() == tmpdir.strpath
537+
538+
539+
class Pickled:
540+
541+
def __getstate__(self):
542+
return self.__dict__
543+
544+
545+
class PickledBreaker:
546+
547+
def __setstate__(self, d):
548+
raise Exception()
549+
550+
551+
def test_versioned_pklization(tmpdir):
552+
tmpdir.chdir()
553+
554+
obj = Pickled()
555+
savepkl('./pickled.pkz', obj, versioning=True)
556+
557+
with pytest.raises(Exception):
558+
with mock.patch('nipype.utils.tests.test_filemanip.Pickled', PickledBreaker), \
559+
mock.patch('nipype.__version__', '0.0.0'):
560+
561+
loadpkl('./pickled.pkz', versioning=True)
562+
563+
564+
def test_unversioned_pklization(tmpdir):
565+
tmpdir.chdir()
566+
567+
obj = Pickled()
568+
savepkl('./pickled.pkz', obj)
569+
570+
with pytest.raises(Exception):
571+
with mock.patch('nipype.utils.tests.test_filemanip.Pickled', PickledBreaker):
572+
loadpkl('./pickled.pkz', versioning=True)

0 commit comments

Comments
 (0)