1
- # -*- coding: utf-8 -*-
2
1
# emacs: -*- mode: python; py-indent-offset: 4; indent-tabs-mode: nil -*-
3
2
# vi: set ft=python sts=4 ts=4 sw=4 et:
4
3
"""
5
4
Nipype interfaces core
6
5
......................
7
6
8
-
9
7
Defines the ``Interface`` API and the body of the
10
8
most basic interfaces.
11
9
The I/O specifications corresponding to these base
12
10
interfaces are found in the ``specs`` module.
13
11
14
12
"""
15
- from copy import deepcopy
16
- from datetime import datetime as dt
17
13
import os
18
- import platform
19
14
import subprocess as sp
20
15
import shlex
21
- import sys
22
16
import simplejson as json
23
- from dateutil .parser import parse as parseutc
24
17
from traits .trait_errors import TraitError
25
18
26
19
from ... import config , logging , LooseVersion
27
20
from ...utils .provenance import write_provenance
28
- from ...utils .misc import str2bool , rgetcwd
29
- from ...utils .filemanip import split_filename , which , get_dependencies , canonicalize_env
21
+ from ...utils .misc import str2bool
22
+ from ...utils .filemanip import (
23
+ canonicalize_env ,
24
+ get_dependencies ,
25
+ indirectory ,
26
+ split_filename ,
27
+ which ,
28
+ )
30
29
from ...utils .subprocess import run_command
31
30
32
31
from ...external .due import due
39
38
MpiCommandLineInputSpec ,
40
39
get_filecopy_info ,
41
40
)
42
- from .support import Bunch , InterfaceResult , NipypeInterfaceError , format_help
41
+ from .support import (
42
+ RuntimeContext ,
43
+ InterfaceResult ,
44
+ NipypeInterfaceError ,
45
+ format_help ,
46
+ )
43
47
44
48
iflogger = logging .getLogger ("nipype.interface" )
45
49
@@ -63,8 +67,15 @@ class Interface(object):
63
67
64
68
"""
65
69
66
- input_spec = None # A traited input specification
67
- output_spec = None # A traited output specification
70
+ input_spec = None
71
+ """
72
+ The specification of the input, defined by a :py:class:`~traits.has_traits.HasTraits` class.
73
+ """
74
+ output_spec = None
75
+ """
76
+ The specification of the output, defined by a :py:class:`~traits.has_traits.HasTraits` class.
77
+ """
78
+
68
79
_can_resume = False # See property below
69
80
_always_run = False # See property below
70
81
@@ -365,131 +376,44 @@ def run(self, cwd=None, ignore_exception=None, **inputs):
365
376
if successful, results
366
377
367
378
"""
368
- from ...utils .profiler import ResourceMonitor
369
-
370
- # if ignore_exception is not provided, taking self.ignore_exception
371
- if ignore_exception is None :
372
- ignore_exception = self .ignore_exception
373
-
374
- # Tear-up: get current and prev directories
375
- syscwd = rgetcwd (error = False ) # Recover when wd does not exist
376
- if cwd is None :
377
- cwd = syscwd
378
-
379
- os .chdir (cwd ) # Change to the interface wd
379
+ rtc = RuntimeContext (
380
+ resource_monitor = config .resource_monitor and self .resource_monitor ,
381
+ ignore_exception = ignore_exception
382
+ if ignore_exception is not None
383
+ else self .ignore_exception ,
384
+ )
380
385
381
- enable_rm = config . resource_monitor and self . resource_monitor
382
- self .inputs .trait_set (** inputs )
386
+ with indirectory ( cwd or os . getcwd ()):
387
+ self .inputs .trait_set (** inputs )
383
388
self ._check_mandatory_inputs ()
384
389
self ._check_version_requirements (self .inputs )
385
- interface = self .__class__
386
- self ._duecredit_cite ()
387
390
388
- # initialize provenance tracking
389
- store_provenance = str2bool (
390
- config .get ("execution" , "write_provenance" , "false" )
391
- )
392
- env = deepcopy (dict (os .environ ))
393
- if self ._redirect_x :
394
- env ["DISPLAY" ] = config .get_display ()
395
-
396
- runtime = Bunch (
397
- cwd = cwd ,
398
- prevcwd = syscwd ,
399
- returncode = None ,
400
- duration = None ,
401
- environ = env ,
402
- startTime = dt .isoformat (dt .utcnow ()),
403
- endTime = None ,
404
- platform = platform .platform (),
405
- hostname = platform .node (),
406
- version = self .version ,
407
- )
408
- runtime_attrs = set (runtime .dictcopy ())
409
-
410
- mon_sp = None
411
- if enable_rm :
412
- mon_freq = float (config .get ("execution" , "resource_monitor_frequency" , 1 ))
413
- proc_pid = os .getpid ()
414
- iflogger .debug (
415
- "Creating a ResourceMonitor on a %s interface, PID=%d." ,
416
- self .__class__ .__name__ ,
417
- proc_pid ,
418
- )
419
- mon_sp = ResourceMonitor (proc_pid , freq = mon_freq )
420
- mon_sp .start ()
391
+ with rtc (self , cwd = cwd , redirect_x = self ._redirect_x ) as runtime :
421
392
422
- # Grab inputs now, as they should not change during execution
423
- inputs = self .inputs .get_traitsfree ()
424
- outputs = None
425
-
426
- try :
393
+ # Grab inputs now, as they should not change during execution
394
+ inputs = self .inputs .get_traitsfree ()
395
+ outputs = None
396
+ # Run interface
427
397
runtime = self ._pre_run_hook (runtime )
428
398
runtime = self ._run_interface (runtime )
429
399
runtime = self ._post_run_hook (runtime )
400
+ # Collect outputs
430
401
outputs = self .aggregate_outputs (runtime )
431
- except Exception as e :
432
- import traceback
433
-
434
- # Retrieve the maximum info fast
435
- runtime .traceback = traceback .format_exc ()
436
- # Gather up the exception arguments and append nipype info.
437
- exc_args = e .args if getattr (e , "args" ) else tuple ()
438
- exc_args += (
439
- "An exception of type %s occurred while running interface %s."
440
- % (type (e ).__name__ , self .__class__ .__name__ ),
441
- )
442
- if config .get ("logging" , "interface_level" , "info" ).lower () == "debug" :
443
- exc_args += ("Inputs: %s" % str (self .inputs ),)
444
-
445
- runtime .traceback_args = ("\n " .join (["%s" % arg for arg in exc_args ]),)
446
-
447
- if not ignore_exception :
448
- raise
449
- finally :
450
- if runtime is None or runtime_attrs - set (runtime .dictcopy ()):
451
- raise RuntimeError (
452
- "{} interface failed to return valid "
453
- "runtime object" .format (interface .__class__ .__name__ )
454
- )
455
- # This needs to be done always
456
- runtime .endTime = dt .isoformat (dt .utcnow ())
457
- timediff = parseutc (runtime .endTime ) - parseutc (runtime .startTime )
458
- runtime .duration = (
459
- timediff .days * 86400 + timediff .seconds + timediff .microseconds / 1e6
460
- )
461
- results = InterfaceResult (
462
- interface , runtime , inputs = inputs , outputs = outputs , provenance = None
463
- )
464
402
465
- # Add provenance (if required)
466
- if store_provenance :
467
- # Provenance will only throw a warning if something went wrong
468
- results .provenance = write_provenance (results )
469
-
470
- # Make sure runtime profiler is shut down
471
- if enable_rm :
472
- import numpy as np
473
-
474
- mon_sp .stop ()
475
-
476
- runtime .mem_peak_gb = None
477
- runtime .cpu_percent = None
478
-
479
- # Read .prof file in and set runtime values
480
- vals = np .loadtxt (mon_sp .fname , delimiter = "," )
481
- if vals .size :
482
- vals = np .atleast_2d (vals )
483
- runtime .mem_peak_gb = vals [:, 2 ].max () / 1024
484
- runtime .cpu_percent = vals [:, 1 ].max ()
485
-
486
- runtime .prof_dict = {
487
- "time" : vals [:, 0 ].tolist (),
488
- "cpus" : vals [:, 1 ].tolist (),
489
- "rss_GiB" : (vals [:, 2 ] / 1024 ).tolist (),
490
- "vms_GiB" : (vals [:, 3 ] / 1024 ).tolist (),
491
- }
492
- os .chdir (syscwd )
403
+ results = InterfaceResult (
404
+ self .__class__ ,
405
+ rtc .runtime ,
406
+ inputs = inputs ,
407
+ outputs = outputs ,
408
+ provenance = None ,
409
+ )
410
+
411
+ # Add provenance (if required)
412
+ if str2bool (config .get ("execution" , "write_provenance" , "false" )):
413
+ # Provenance will only throw a warning if something went wrong
414
+ results .provenance = write_provenance (results )
415
+
416
+ self ._duecredit_cite ()
493
417
494
418
return results
495
419
0 commit comments