Skip to content
Merged
82 changes: 55 additions & 27 deletions pyneuroml/pynml.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
from __future__ import absolute_import
from __future__ import print_function
from __future__ import unicode_literals
import warnings
import os
import shutil
import sys
Expand Down Expand Up @@ -68,7 +69,7 @@ def parse_arguments():
from neuromllite.GraphVizHandler import engines

engine_info = "\nAvailable engines: %s\n" % str(engines)
except:
except Exception:
engine_info = ""

parser = argparse.ArgumentParser(
Expand Down Expand Up @@ -603,12 +604,13 @@ def generate_lemsgraph(lems_file_name, verbose_generate=True):
verbose=verbose_generate,
report_jnml_output=verbose_generate,
exit_on_fail=True,
return_string=False,
)


def validate_neuroml1(nml1_file_name, verbose_validate=True):
# type: (str, bool) -> bool
# TODO: mark as deprecated
def validate_neuroml1(nml1_file_name, verbose_validate=True,
return_string=False):
# type: (str, bool, bool) -> typing.Union[bool, typing.Tuple[bool, str]]
"""Validate a NeuroML v1 file.

NOTE: NeuroML v1 is deprecated. Please use NeuroML v2.
Expand All @@ -618,24 +620,31 @@ def validate_neuroml1(nml1_file_name, verbose_validate=True):
:type nml1_file_name: str
:param verbose_validate: whether jnml should print verbose information while validating
:type verbose_validate: bool (default: True)
:returns bool: True of jnml ran without errors, exits without a return if jnml fails
:param return_string: toggle to enable or disable returning the output of the jnml validation
:type return_string: bool
:returns: Either a bool, or a tuple (bool, str): True if jnml ran without errors, false if jnml fails; along with the message returned by jnml
"""
logger.info("NOTE: NeuroMLv1 is deprecated. Please use NeuroMLv2.")
pre_args = "-validatev1"
post_args = ""

warnings.warn("Please note that NeuroMLv1 is deprecated. Functions supporting NeuroMLv1 will be removed in the future. Please use NeuroMLv2.", FutureWarning, stacklevel=2)

return run_jneuroml(
pre_args,
nml1_file_name,
post_args,
verbose=verbose_validate,
report_jnml_output=verbose_validate,
exit_on_fail=False,
return_string=return_string,
)


def validate_neuroml2(nml2_file_name, verbose_validate=True, max_memory=None):
# type: (str, bool, str) -> bool
def validate_neuroml2(
nml2_file_name, verbose_validate=True, max_memory=None, return_string=False
):
# type: (str, bool, str, bool) -> typing.Union[bool, typing.Tuple[bool, str]]
"""Validate a NeuroML2 file using jnml.

:params nml2_file_name: name of NeuroML 2 file to validate
Expand All @@ -644,7 +653,9 @@ def validate_neuroml2(nml2_file_name, verbose_validate=True, max_memory=None):
:type verbose_validate: bool (default: True)
:param max_memory: maximum memory the JVM should use while running jnml
:type max_memory: str
:returns bool: True of jnml ran without errors, exits without a return if jnml fails
:param return_string: toggle to enable or disable returning the output of the jnml validation
:type return_string: bool
:returns: Either a bool, or a tuple (bool, str): True if jnml ran without errors, false if jnml fails; along with the message returned by jnml
"""
pre_args = "-validate"
post_args = ""
Expand All @@ -658,6 +669,7 @@ def validate_neuroml2(nml2_file_name, verbose_validate=True, max_memory=None):
verbose=verbose_validate,
report_jnml_output=verbose_validate,
exit_on_fail=False,
return_string=return_string,
)
else:
return run_jneuroml(
Expand All @@ -667,6 +679,7 @@ def validate_neuroml2(nml2_file_name, verbose_validate=True, max_memory=None):
verbose=verbose_validate,
report_jnml_output=verbose_validate,
exit_on_fail=False,
return_string=return_string,
)


Expand Down Expand Up @@ -2134,9 +2147,10 @@ def run_jneuroml(
exec_in_dir=".",
verbose=DEFAULTS["v"],
report_jnml_output=True,
exit_on_fail=True,
exit_on_fail=False,
return_string=False,
):
# type: (str, str, str, str, str, bool, bool, bool) -> bool
# type: (str, str, str, str, str, bool, bool, bool, bool) -> typing.Union[typing.Tuple[int, str], bool]
"""Run jnml with provided arguments.

:param pre_args: pre-file name arguments
Expand All @@ -2153,6 +2167,11 @@ def run_jneuroml(
:type report_jnml_output: bool
:param exit_on_fail: toggle whether command should exit if jnml fails
:type exit_on_fail: bool
:param return_string: toggle whether the output string should be returned
:type return_string: bool

:returns: either a bool, or a Tuple (bool, str) depending on the value of return_string: True of jnml ran successfully, False if not; along with the output of the command

"""
logger.debug(
"Running jnml on %s with pre args: [%s], post args: [%s], in dir: %s, verbose: %s, report: %s, exit on fail: %s"
Expand All @@ -2166,13 +2185,14 @@ def run_jneuroml(
exit_on_fail,
)
)
if "nogui" in post_args and not os.name == "nt":
if post_args and "nogui" in post_args and not os.name == "nt":
pre_jar = " -Djava.awt.headless=true"
else:
pre_jar = ""

jar_path = get_path_to_jnml_jar()
output = ""
retcode = -1

try:
command = 'java -Xmx%s %s -jar "%s" %s "%s" %s' % (
Expand All @@ -2183,16 +2203,19 @@ def run_jneuroml(
target_file,
post_args,
)
output = execute_command_in_dir(
retcode, output = execute_command_in_dir(
command, exec_in_dir, verbose=verbose, prefix=" jNeuroML >> "
)

if not output:
if retcode != 0:
if exit_on_fail:
logger.error("execute_command_in_dir returned with output: %s" % output)
sys.exit(-1)
sys.exit(retcode)
else:
return False
if return_string:
return (False, output)
else:
return False

if report_jnml_output:
logger.debug(
Expand All @@ -2212,8 +2235,14 @@ def run_jneuroml(
if exit_on_fail:
sys.exit(-1)
else:
return False
return True
if return_string:
return (False, output)
else:
return False
if return_string:
return (True, output)
else:
return True


# TODO: Refactorinng
Expand Down Expand Up @@ -2245,7 +2274,7 @@ def run_jneuroml_with_realtime_output(
:param exit_on_fail: toggle whether command should exit if jnml fails
:type exit_on_fail: bool
"""
if "nogui" in post_args and not os.name == "nt":
if post_args and "nogui" in post_args and not os.name == "nt":
pre_jar = " -Djava.awt.headless=true"
else:
pre_jar = ""
Expand Down Expand Up @@ -2337,7 +2366,7 @@ def execute_command_in_dir_with_realtime_output(
def execute_command_in_dir(
command, directory, verbose=DEFAULTS["v"], prefix="Output: ", env=None
):
# type: (str, str, bool, str, typing.Union[None, str]) -> typing.Union[None, str]
# type: (str, str, bool, str, typing.Union[None, str]) -> typing.Tuple(int, str)
"""Execute a command in specific working directory

:param command: command to run
Expand All @@ -2346,11 +2375,12 @@ def execute_command_in_dir(
:type directory: str
:param verbose: toggle verbose output
:type verbose: bool
:param prefix: string to prefix output with
:param prefix: string to prefix console output with
:type prefix: str
:param env: environment variables to be used
:type env: str
"""
return_string = "" # type: typing.Union[bytes, str]
if os.name == "nt":
directory = os.path.normpath(directory)

Expand Down Expand Up @@ -2379,7 +2409,7 @@ def execute_command_in_dir(
"Command completed. Output: \n %s%s"
% (prefix, return_string.replace("\n", "\n " + prefix))
)
return return_string
return (0, return_string)

except AttributeError:
# For python 2.6...
Expand All @@ -2388,19 +2418,17 @@ def execute_command_in_dir(
return_string = subprocess.Popen(
command, cwd=directory, shell=True, stdout=subprocess.PIPE
).communicate()[0]
return return_string
return return_string.decode("utf-8")

except subprocess.CalledProcessError as e:
logger.critical("*** Problem running command: \n %s" % e)
logger.critical(
"%s%s" % (prefix, e.output.decode().replace("\n", "\n" + prefix))
)
return None
except:
return (e.returncode, e.output.decode())
except Exception as e:
logger.critical("*** Unknown problem running command: %s" % e)
return None

logger.info("Finished execution")
return (-1, str(e))


def generate_plot(
Expand Down
59 changes: 59 additions & 0 deletions tests/test_pynml.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,9 @@
extract_lems_definition_files,
list_exposures,
list_recording_paths_for_exposures,
execute_command_in_dir,
run_jneuroml,
validate_neuroml2
)


Expand Down Expand Up @@ -95,6 +98,62 @@ def test_recording_path_listing_2(self):
print("\n".join(paths))
os.chdir("../")

def test_execute_command_in_dir(self):
"""Test execute_command_in_dir function."""
command = "ls"
exec_in_dir = "."
verbose = True
output = None
retcode = None

retcode, output = execute_command_in_dir(
command, exec_in_dir, verbose=verbose, prefix=" jNeuroML >> "
)

self.assertEqual(retcode, 0)
self.assertIsNotNone(output)

command_bad = "ls non_existent_file"
output = None
retcode = None
retcode, output = execute_command_in_dir(
command_bad, exec_in_dir, verbose=verbose, prefix=" jNeuroML >> "
)
self.assertNotEqual(retcode, 0)
self.assertIsNotNone(output)

def test_run_jneuroml(self):
"""Test run_jneuroml"""
retstat = None
retstat = run_jneuroml("-v", None, None)
self.assertTrue(retstat)

retstat = None
retstat = run_jneuroml("-randomflag", "", "")
self.assertFalse(retstat)

def test_validate_neuroml2(self):
"""Test validate_neuroml2"""
os.chdir("tests/")
retval = None
retval = validate_neuroml2("HH_example_k_channel.nml")
self.assertTrue(retval)

retval = None
retstring = None
retval, retstring = validate_neuroml2("HH_example_k_channel.nml", return_string=True)
self.assertTrue(retval)
self.assertIn("Valid against schema and all tests", retstring)
os.chdir("../")

retval = None
retstring = None
retval, retstring = validate_neuroml2("setup.py", return_string=True)
self.assertFalse(retval)
self.assertIn("1 failed", retstring)

# TODO: add similar validation for NeuroMLv1


if __name__ == "__main__":
unittest.main()