From fa2b5c95a720a060915a026ba727005c206d6910 Mon Sep 17 00:00:00 2001 From: Satrajit Ghosh Date: Sun, 13 Aug 2017 14:59:48 -0400 Subject: [PATCH] Revert "fix: python 2 Function interfaces recompatibility" --- doc/users/saving_workflows.rst | 2 +- nipype/interfaces/utility/wrappers.py | 2 +- nipype/pipeline/engine/utils.py | 5 ++- nipype/pipeline/engine/workflows.py | 4 +-- nipype/utils/functions.py | 47 --------------------------- nipype/utils/misc.py | 43 +++++++++++++++++++++++- nipype/utils/tests/test_functions.py | 41 ----------------------- nipype/utils/tests/test_misc.py | 25 ++++++++++++-- 8 files changed, 71 insertions(+), 98 deletions(-) delete mode 100644 nipype/utils/functions.py delete mode 100644 nipype/utils/tests/test_functions.py diff --git a/doc/users/saving_workflows.rst b/doc/users/saving_workflows.rst index 33d1e8a118..c97751eead 100644 --- a/doc/users/saving_workflows.rst +++ b/doc/users/saving_workflows.rst @@ -55,7 +55,7 @@ This will create a file "outputtestsave.py" with the following content: from nipype.pipeline.engine import Workflow, Node, MapNode from nipype.interfaces.utility import IdentityInterface from nipype.interfaces.utility import Function - from nipype.utils.functions import getsource + from nipype.utils.misc import getsource from nipype.interfaces.fsl.preprocess import BET from nipype.interfaces.fsl.utils import ImageMaths # Functions diff --git a/nipype/interfaces/utility/wrappers.py b/nipype/interfaces/utility/wrappers.py index 6885d7218e..4de11d7ea8 100644 --- a/nipype/interfaces/utility/wrappers.py +++ b/nipype/interfaces/utility/wrappers.py @@ -24,7 +24,7 @@ BaseInterfaceInputSpec, get_max_resources_used) from ..io import IOBase, add_traits from ...utils.filemanip import filename_to_list -from ...utils.functions import getsource, create_function_from_source +from ...utils.misc import getsource, create_function_from_source logger = logging.getLogger('interface') if runtime_profile: diff --git a/nipype/pipeline/engine/utils.py b/nipype/pipeline/engine/utils.py index fe8228c8ac..25b12ab607 100644 --- a/nipype/pipeline/engine/utils.py +++ b/nipype/pipeline/engine/utils.py @@ -31,8 +31,7 @@ from ...utils.filemanip import (fname_presuffix, FileNotFoundError, to_str, filename_to_list, get_related_files) -from ...utils.misc import str2bool -from ...utils.functions import create_function_from_source +from ...utils.misc import create_function_from_source, str2bool from ...interfaces.base import (CommandLine, isdefined, Undefined, InterfaceResult) from ...interfaces.utility import IdentityInterface @@ -101,7 +100,7 @@ def _write_inputs(node): lines[-1] = lines[-1].replace(' %s(' % funcname, ' %s_1(' % funcname) funcname = '%s_1' % funcname - lines.append('from nipype.utils.functions import getsource') + lines.append('from nipype.utils.misc import getsource') lines.append("%s.inputs.%s = getsource(%s)" % (nodename, key, funcname)) diff --git a/nipype/pipeline/engine/workflows.py b/nipype/pipeline/engine/workflows.py index 14c4920a72..f30ed50051 100644 --- a/nipype/pipeline/engine/workflows.py +++ b/nipype/pipeline/engine/workflows.py @@ -36,8 +36,8 @@ from ... import config, logging -from ...utils.misc import (unflatten, package_check, str2bool) -from ...utils.functions import (getsource, create_function_from_source) +from ...utils.misc import (unflatten, package_check, str2bool, + getsource, create_function_from_source) from ...interfaces.base import (traits, InputMultiPath, CommandLine, Undefined, TraitedSpec, DynamicTraitedSpec, Bunch, InterfaceResult, md5, Interface, diff --git a/nipype/utils/functions.py b/nipype/utils/functions.py deleted file mode 100644 index aa72d85009..0000000000 --- a/nipype/utils/functions.py +++ /dev/null @@ -1,47 +0,0 @@ -# -*- coding: utf-8 -*- -""" -Handles custom functions used in Function interface. Future imports -are avoided to keep namespace as clear as possible. -""" -from builtins import next, str -from future.utils import raise_from -import inspect -from textwrap import dedent - -def getsource(function): - """Returns the source code of a function""" - return dedent(inspect.getsource(function)) - - -def create_function_from_source(function_source, imports=None): - """Return a function object from a function source - - Parameters - ---------- - function_source : unicode string - unicode string defining a function - imports : list of strings - list of import statements in string form that allow the function - to be executed in an otherwise empty namespace - """ - ns = {} - import_keys = [] - - try: - if imports is not None: - for statement in imports: - exec(statement, ns) - import_keys = list(ns.keys()) - exec(function_source, ns) - - except Exception as e: - msg = 'Error executing function\n{}\n'.format(function_source) - msg += ("Functions in connection strings have to be standalone. " - "They cannot be declared either interactively or inside " - "another function or inline in the connect string. Any " - "imports should be done inside the function.") - raise_from(RuntimeError(msg), e) - ns_funcs = list(set(ns) - set(import_keys + ['__builtins__'])) - assert len(ns_funcs) == 1, "Function or inputs are ill-defined" - func = ns[ns_funcs[0]] - return func diff --git a/nipype/utils/misc.py b/nipype/utils/misc.py index 095e6b88f3..552e24c435 100644 --- a/nipype/utils/misc.py +++ b/nipype/utils/misc.py @@ -3,7 +3,7 @@ # vi: set ft=python sts=4 ts=4 sw=4 et: """Miscellaneous utility functions """ -from __future__ import print_function, unicode_literals, division, absolute_import +from __future__ import print_function, division, unicode_literals, absolute_import from future import standard_library standard_library.install_aliases() from builtins import next, str @@ -66,6 +66,47 @@ def trim(docstring, marker=None): return '\n'.join(trimmed) +def getsource(function): + """Returns the source code of a function""" + src = dedent(inspect.getsource(function)) + return src + + +def create_function_from_source(function_source, imports=None): + """Return a function object from a function source + + Parameters + ---------- + function_source : pickled string + string in pickled form defining a function + imports : list of strings + list of import statements in string form that allow the function + to be executed in an otherwise empty namespace + """ + ns = {} + import_keys = [] + try: + if imports is not None: + for statement in imports: + exec(statement, ns) + import_keys = list(ns.keys()) + exec(function_source, ns) + + except Exception as e: + msg = '\nError executing function:\n %s\n' % function_source + msg += '\n'.join(["Functions in connection strings have to be standalone.", + "They cannot be declared either interactively or inside", + "another function or inline in the connect string. Any", + "imports should be done inside the function" + ]) + raise_from(RuntimeError(msg), e) + ns_funcs = list(set(ns) - set(import_keys + ['__builtins__'])) + assert len(ns_funcs) == 1, "Function or inputs are ill-defined" + funcname = ns_funcs[0] + func = ns[funcname] + return func + + def find_indices(condition): "Return the indices where ravel(condition) is true" res, = np.nonzero(np.ravel(condition)) diff --git a/nipype/utils/tests/test_functions.py b/nipype/utils/tests/test_functions.py deleted file mode 100644 index 1d9b9dac7a..0000000000 --- a/nipype/utils/tests/test_functions.py +++ /dev/null @@ -1,41 +0,0 @@ -# -*- coding: utf-8 -*- -import sys -import pytest -from nipype.utils.functions import (getsource, create_function_from_source) - -def _func1(x): - return x**3 - -def test_func_to_str(): - - def func1(x): - return x**2 - - # Should be ok with both functions! - for f in _func1, func1: - f_src = getsource(f) - f_recreated = create_function_from_source(f_src) - assert f(2.3) == f_recreated(2.3) - -def test_func_to_str_err(): - bad_src = "obbledygobbledygook" - with pytest.raises(RuntimeError): create_function_from_source(bad_src) - -def _print_statement(): - try: - exec('print ""') - return True - except SyntaxError: - return False - -def test_func_string(): - def is_string(): - return isinstance('string', str) - - wrapped_func = create_function_from_source(getsource(is_string)) - assert is_string() == wrapped_func() - -@pytest.mark.skipif(sys.version_info[0] > 2, reason="breaks python 3") -def test_func_print_py2(): - wrapped_func = create_function_from_source(getsource(_print_statement)) - assert wrapped_func() diff --git a/nipype/utils/tests/test_misc.py b/nipype/utils/tests/test_misc.py index 1685fd645e..f2780a584f 100644 --- a/nipype/utils/tests/test_misc.py +++ b/nipype/utils/tests/test_misc.py @@ -8,8 +8,9 @@ import pytest -from nipype.utils.misc import (container_to_string, str2bool, - flatten, unflatten) +from nipype.utils.misc import (container_to_string, getsource, + create_function_from_source, str2bool, flatten, + unflatten) def test_cont_to_str(): @@ -34,6 +35,26 @@ def test_cont_to_str(): assert (container_to_string(123) == '123') +def _func1(x): + return x**3 + + +def test_func_to_str(): + + def func1(x): + return x**2 + + # Should be ok with both functions! + for f in _func1, func1: + f_src = getsource(f) + f_recreated = create_function_from_source(f_src) + assert f(2.3) == f_recreated(2.3) + +def test_func_to_str_err(): + bad_src = "obbledygobbledygook" + with pytest.raises(RuntimeError): create_function_from_source(bad_src) + + @pytest.mark.parametrize("string, expected", [ ("yes", True), ("true", True), ("t", True), ("1", True), ("no", False), ("false", False), ("n", False), ("f", False), ("0", False)