From 21918cbcb9638b0fcb0b1d99720375c7907b6183 Mon Sep 17 00:00:00 2001 From: "Ankur Sinha (Ankur Sinha Gmail)" Date: Tue, 23 Apr 2024 15:00:22 +0100 Subject: [PATCH] feat(optional-deps): add warnings to inform users of optional packages --- .pre-commit-config.yaml | 1 + pyneuroml/analysis/__init__.py | 15 ++++++---- pyneuroml/annotations.py | 11 ++++--- pyneuroml/biosimulations.py | 15 +++++++--- pyneuroml/neuron/__init__.py | 11 ++++--- pyneuroml/neuron/nrn_export_utils.py | 12 ++++++-- pyneuroml/nsgr.py | 8 ++++-- pyneuroml/plot/PlotMorphologyPlotly.py | 7 ++++- pyneuroml/plot/PlotMorphologyVispy.py | 17 +++++++---- pyneuroml/plot/PlotSpikes.py | 10 +++++-- pyneuroml/povray/MakeMovie.py | 11 +++++-- pyneuroml/sbml/__init__.py | 15 ++++++++-- pyneuroml/tellurium/__init__.py | 23 ++++++++++++--- pyneuroml/tune/NeuroMLController.py | 40 ++++++++------------------ pyneuroml/tune/NeuroMLTuner.py | 22 ++++++++++---- pyneuroml/utils/__init__.py | 2 +- setup.cfg | 1 - 17 files changed, 148 insertions(+), 73 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 96f3cc20..a7dca78e 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -11,4 +11,5 @@ repos: rev: v0.4.1 hooks: - id: ruff + args: [ --exit-zero ] - id: ruff-format diff --git a/pyneuroml/analysis/__init__.py b/pyneuroml/analysis/__init__.py index 1011e8fd..880a8308 100644 --- a/pyneuroml/analysis/__init__.py +++ b/pyneuroml/analysis/__init__.py @@ -10,12 +10,19 @@ from pyneuroml.utils.plot import get_next_hex_color from pyneuroml.plot import generate_plot import neuroml as nml -from pyelectro.analysis import max_min -from pyelectro.analysis import mean_spike_frequency + from typing import Optional logger = logging.getLogger(__name__) +logger.setLevel(logging.INFO) + +try: + from pyelectro.analysis import max_min + from pyelectro.analysis import mean_spike_frequency +except ImportError: + logger.warning("Please install optional dependencies to use analysis features:") + logger.warning("pip install pyneuroml[analysis]") def generate_current_vs_frequency_curve( @@ -202,9 +209,7 @@ def generate_current_vs_frequency_curve( stims = [] if len(custom_amps_nA) > 0: stims = [float(a) for a in custom_amps_nA] - stim_info = [ - "%snA" % float(a) for a in custom_amps_nA - ] # type: typing.Union[str, typing.List[str]] + stim_info = ["%snA" % float(a) for a in custom_amps_nA] # type: typing.Union[str, typing.List[str]] else: # else generate a list using the provided arguments amp = start_amp_nA diff --git a/pyneuroml/annotations.py b/pyneuroml/annotations.py index 7071a7b2..4a0283c7 100644 --- a/pyneuroml/annotations.py +++ b/pyneuroml/annotations.py @@ -7,20 +7,23 @@ Copyright 2024 NeuroML contributors """ - import logging import pprint import typing from lxml import etree -from rdflib import RDF, BNode, Graph, Literal, Namespace, URIRef -from rdflib.namespace import DC, DCTERMS, FOAF, RDF, RDFS - from pyneuroml.utils.xml import _find_elements, _get_attr_in_element logger = logging.getLogger(__name__) logger.setLevel(logging.INFO) +try: + from rdflib import BNode, Graph, Literal, Namespace, URIRef + from rdflib.namespace import DC, DCTERMS, FOAF, RDFS +except ImportError: + logger.warning("Please install optional dependencies to use annotation features:") + logger.warning("pip install pyneuroml[annotations]") + # From https://docs.biosimulations.org/concepts/conventions/simulation-project-metadata/ PREDICATES_MAP = { diff --git a/pyneuroml/biosimulations.py b/pyneuroml/biosimulations.py index ce9bad50..279a1ca2 100644 --- a/pyneuroml/biosimulations.py +++ b/pyneuroml/biosimulations.py @@ -7,14 +7,11 @@ Copyright 2024 NeuroML contributors """ - import logging import typing from datetime import datetime import requests -from pydantic import BaseModel -from requests_toolbelt.multipart.encoder import MultipartEncoder from pyneuroml import __version__ from pyneuroml.annotations import create_annotation @@ -24,6 +21,16 @@ logger = logging.getLogger(__name__) logger.setLevel(logging.DEBUG) +try: + from pydantic import BaseModel + from requests_toolbelt.multipart.encoder import MultipartEncoder +except ImportError: + logger.warning( + "Please install optional dependencies to use Biosimulation.org features:" + ) + logger.warning("pip install pyneuroml[combine]") + + biosimulators_api_url = "https://api.biosimulators.org" biosimulations_api_url = "https://api.biosimulations.org" @@ -59,7 +66,7 @@ def get_simulator_versions( "xpp", "brian2", "copasi", - ] + ], ) -> typing.Dict[str, typing.List[str]]: """Get simulator list from biosimulators. diff --git a/pyneuroml/neuron/__init__.py b/pyneuroml/neuron/__init__.py index cbbbee65..3aed594d 100644 --- a/pyneuroml/neuron/__init__.py +++ b/pyneuroml/neuron/__init__.py @@ -25,14 +25,17 @@ from pyneuroml.pynml import validate_neuroml1 from pyneuroml.pynml import validate_neuroml2 -from pyneuroml.neuron.nrn_export_utils import set_erev_for_mechanism -from neuron import h - - pp = pprint.PrettyPrinter(depth=4) logger = logging.getLogger(__name__) logger.setLevel(logging.INFO) +try: + from neuron import h + from pyneuroml.neuron.nrn_export_utils import set_erev_for_mechanism +except ImportError: + logger.warning("Please install optional dependencies to use NEURON features:") + logger.warning("pip install pyneuroml[neuron]") + def get_utils_hoc() -> pathlib.Path: """Get full path of utils.hoc file diff --git a/pyneuroml/neuron/nrn_export_utils.py b/pyneuroml/neuron/nrn_export_utils.py index c61c3861..ac5c4b9d 100644 --- a/pyneuroml/neuron/nrn_export_utils.py +++ b/pyneuroml/neuron/nrn_export_utils.py @@ -6,9 +6,17 @@ """ +import logging -from neuron import h -from nrn import * +logger = logging.getLogger(__name__) +logger.setLevel(logging.INFO) + +try: + from neuron import h + from nrn import * +except ImportError: + logger.warning("Please install optional dependencies to use NEURON features:") + logger.warning("pip install pyneuroml[neuron]") def clear_neuron() -> None: diff --git a/pyneuroml/nsgr.py b/pyneuroml/nsgr.py index 1de25038..73238730 100644 --- a/pyneuroml/nsgr.py +++ b/pyneuroml/nsgr.py @@ -7,7 +7,6 @@ Copyright 2024 NeuroML contributors """ - import logging import os import pathlib @@ -15,11 +14,16 @@ from zipfile import ZipFile from pyneuroml.runners import generate_sim_scripts_in_folder -from pynsgr.commands.nsgr_submit import nsgr_submit logger = logging.getLogger(__name__) logger.setLevel(logging.DEBUG) +try: + from pynsgr.commands.nsgr_submit import nsgr_submit +except ImportError: + logger.warning("Please install optional dependencies to use NSG features:") + logger.warning("pip install pyneuroml[nsgr]") + def run_on_nsg( engine: str, diff --git a/pyneuroml/plot/PlotMorphologyPlotly.py b/pyneuroml/plot/PlotMorphologyPlotly.py index b3d2454c..2b906dd9 100644 --- a/pyneuroml/plot/PlotMorphologyPlotly.py +++ b/pyneuroml/plot/PlotMorphologyPlotly.py @@ -12,13 +12,18 @@ import os import typing import logging -import plotly.graph_objects as go from neuroml import Cell, NeuroMLDocument from pyneuroml.pynml import read_neuroml2_file logger = logging.getLogger(__name__) logger.setLevel(logging.INFO) +try: + import plotly.graph_objects as go +except ImportError: + logger.warning("Please install optional dependencies to use plotly features:") + logger.warning("pip install pyneuroml[plotly]") + def plot_3D_cell_morphology_plotly( nml_file: typing.Union[str, Cell, NeuroMLDocument], diff --git a/pyneuroml/plot/PlotMorphologyVispy.py b/pyneuroml/plot/PlotMorphologyVispy.py index 7e832023..6cb42dbc 100644 --- a/pyneuroml/plot/PlotMorphologyVispy.py +++ b/pyneuroml/plot/PlotMorphologyVispy.py @@ -9,7 +9,6 @@ Copyright 2023 NeuroML contributors """ - import logging import math import random @@ -28,16 +27,22 @@ load_minimal_morphplottable__model, ) from scipy.spatial.transform import Rotation -from vispy import app, scene, use -from vispy.geometry.generation import create_sphere -from vispy.geometry.meshdata import MeshData -from vispy.scene.visuals import InstancedMesh -from vispy.util.transforms import rotate from typing import Optional logger = logging.getLogger(__name__) logger.setLevel(logging.INFO) +try: + from vispy import app, scene, use + from vispy.geometry.generation import create_sphere + from vispy.geometry.meshdata import MeshData + from vispy.scene.visuals import InstancedMesh + from vispy.util.transforms import rotate +except ImportError: + logger.warning("Please install optional dependencies to use vispy features:") + logger.warning("pip install pyneuroml[vispy]") + logger.warning("or (for Qt5):") + logger.warning("pip install pyneuroml[vispy-qt5]") VISPY_THEME = { "light": {"bg": "white", "fg": "black"}, diff --git a/pyneuroml/plot/PlotSpikes.py b/pyneuroml/plot/PlotSpikes.py index 6e4bc9be..0dfe065d 100644 --- a/pyneuroml/plot/PlotSpikes.py +++ b/pyneuroml/plot/PlotSpikes.py @@ -6,6 +6,7 @@ Copyright 2023 NeuroML contributors """ + import argparse import logging import os @@ -18,6 +19,13 @@ from pyneuroml.utils.cli import build_namespace logger = logging.getLogger(__name__) +logger.setLevel(logging.INFO) + +try: + import tables # pytables for HDF5 support +except ImportError: + logger.warning("Please install optional dependencies to use hdf5 features:") + logger.warning("pip install pyneuroml[hdf5]") FORMAT_ID_T = "id_t" FORMAT_ID_TIME_NEST_DAT = "id_t_nest_dat" @@ -121,8 +129,6 @@ def read_sonata_spikes_hdf5_file(file_name: str): full_path = os.path.abspath(file_name) logger.info("Loading SONATA spike times from: %s (%s)" % (file_name, full_path)) - import tables # pytables for HDF5 support - h5file = tables.open_file(file_name, mode="r") sorting = ( diff --git a/pyneuroml/povray/MakeMovie.py b/pyneuroml/povray/MakeMovie.py index 618e2474..7418d21f 100644 --- a/pyneuroml/povray/MakeMovie.py +++ b/pyneuroml/povray/MakeMovie.py @@ -1,11 +1,9 @@ -import cv2 import colorsys import argparse import sys import os.path import logging - """ STILL IN DEVELOPMENT @@ -18,6 +16,15 @@ logger = logging.getLogger(__name__) +logger.setLevel(logging.INFO) + +try: + import cv2 +except ImportError: + logger.warning("Please install optional dependencies to use povray features:") + logger.warning("pip install pyneuroml[povray]") + + scale_font = 1 font = cv2.FONT_HERSHEY_COMPLEX_SMALL diff --git a/pyneuroml/sbml/__init__.py b/pyneuroml/sbml/__init__.py index e2f32afc..d4dad9f5 100644 --- a/pyneuroml/sbml/__init__.py +++ b/pyneuroml/sbml/__init__.py @@ -5,11 +5,22 @@ import os import errno -import libsbml -from libsbml import SBMLReader from typing import List +import logging + +logger = logging.getLogger(__name__) +logger.setLevel(logging.INFO) + +try: + import libsbml + from libsbml import SBMLReader +except ImportError: + logger.warning("Please install optional dependencies to use SBML features:") + logger.warning("pip install pyneuroml[combine]") + + def validate_sbml_files(input_files: List[str], strict_units: bool = False) -> bool: """Validate input files using libsbml.SBMLDocument.checkConsistency diff --git a/pyneuroml/tellurium/__init__.py b/pyneuroml/tellurium/__init__.py index edaa8f48..bdebfa37 100644 --- a/pyneuroml/tellurium/__init__.py +++ b/pyneuroml/tellurium/__init__.py @@ -2,13 +2,28 @@ run a model using the tellurium engine """ +import os +import logging from pyneuroml.sedml import validate_sedml_files -import tellurium as te -te.setDefaultPlottingEngine("matplotlib") -import os -import libsedml +logger = logging.getLogger(__name__) +logger.setLevel(logging.INFO) + +try: + import tellurium as te + + te.setDefaultPlottingEngine("matplotlib") +except ImportError: + logger.warning("Please install optional dependencies to use Tellurium features:") + logger.warning("pip install pyneuroml[tellurium]") + +try: + import libsedml +except ImportError: + logger.warning("Please install optional dependencies to use SED-ML features:") + logger.warning("pip install pyneuroml[combine]") + # # For technical reasons, any software which uses libSEDML # # must provide a custom build - Tellurium uses tesedml diff --git a/pyneuroml/tune/NeuroMLController.py b/pyneuroml/tune/NeuroMLController.py index c13fe57f..19c60c8a 100644 --- a/pyneuroml/tune/NeuroMLController.py +++ b/pyneuroml/tune/NeuroMLController.py @@ -13,12 +13,7 @@ import logging import pprint -import io - -if sys.version_info.major == 2: - from StringIO import StringIO -else: - from io import StringIO +from io import StringIO from collections import OrderedDict import pyneuroml.pynml @@ -27,8 +22,15 @@ logger = logging.getLogger(__name__) +logger.setLevel(logging.INFO) pp = pprint.PrettyPrinter(indent=4) +try: + from pyelectro import analysis +except ImportError: + logger.warning("Please install optional dependencies to use analysis features:") + logger.warning("pip install pyneuroml[analysis]") + class NeuroMLController: """A Neurotune controller specific to NeuroML. @@ -345,22 +347,14 @@ def run_individual( chanDens.cond_density = "%s %s" % (value, units) elif variable == "vShift_channelDensity": chanDens = None - for ( - cd - ) in ( - cell.biophysical_properties.membrane_properties.channel_density_v_shifts - ): + for cd in cell.biophysical_properties.membrane_properties.channel_density_v_shifts: if cd.id == id2: chanDens = cd chanDens.v_shift = "%s %s" % (value, units) elif variable == "channelDensityNernst": chanDens = None - for ( - cd - ) in ( - cell.biophysical_properties.membrane_properties.channel_density_nernsts - ): + for cd in cell.biophysical_properties.membrane_properties.channel_density_nernsts: if cd.id == id2: chanDens = cd @@ -391,11 +385,7 @@ def run_individual( chanDens.erev = "%s %s" % (value, units) elif variable == "specificCapacitance": specCap = None - for ( - sc - ) in ( - cell.biophysical_properties.membrane_properties.specific_capacitances - ): + for sc in cell.biophysical_properties.membrane_properties.specific_capacitances: if ( sc.segment_groups is None and id2 == "all" ) or sc.segment_groups == id2: @@ -404,11 +394,7 @@ def run_individual( specCap.value = "%s %s" % (value, units) elif variable == "resistivity": resistivity = None - for ( - rs - ) in ( - cell.biophysical_properties.intracellular_properties.resistivities - ): + for rs in cell.biophysical_properties.intracellular_properties.resistivities: if ( rs.segment_groups is None and id2 == "all" ) or rs.segment_groups == id2: @@ -523,8 +509,6 @@ def run_individual( else: t, v = cont.run_individual(sim_vars, show=(not nogui)) - from pyelectro import analysis - analysis_var = { "peak_delta": 0, "baseline": 0, diff --git a/pyneuroml/tune/NeuroMLTuner.py b/pyneuroml/tune/NeuroMLTuner.py index c938d72f..59be3756 100644 --- a/pyneuroml/tune/NeuroMLTuner.py +++ b/pyneuroml/tune/NeuroMLTuner.py @@ -27,17 +27,29 @@ import pprint import typing -from pyelectro import analysis from matplotlib import pyplot as plt -from neurotune import optimizers -from neurotune import evaluators -from neurotune import utils -from pyneuroml.tune.NeuroMLController import NeuroMLController from pyneuroml.utils.cli import build_namespace from pyneuroml import print_v pp = pprint.PrettyPrinter(indent=4) logger = logging.getLogger(__name__) +logger.setLevel(logging.INFO) + + +try: + from neurotune import optimizers + from neurotune import evaluators + from neurotune import utils + from pyneuroml.tune.NeuroMLController import NeuroMLController +except ImportError: + logger.warning("Please install optional dependencies to use neurotune features:") + logger.warning("pip install pyneuroml[tune]") + +try: + from pyelectro import analysis +except ImportError: + logger.warning("Please install optional dependencies to use analysis features:") + logger.warning("pip install pyneuroml[analysis]") DEFAULTS = { diff --git a/pyneuroml/utils/__init__.py b/pyneuroml/utils/__init__.py index 59b3e3c9..85dbb784 100644 --- a/pyneuroml/utils/__init__.py +++ b/pyneuroml/utils/__init__.py @@ -36,7 +36,7 @@ try: import libsedml except ModuleNotFoundError: - logger.warning("Please install optional dependencies to use libsedml:") + logger.warning("Please install optional dependencies to use SED-ML features:") logger.warning("pip install pyneuroml[combine]") diff --git a/setup.cfg b/setup.cfg index e37e2ba7..1b56e431 100644 --- a/setup.cfg +++ b/setup.cfg @@ -145,7 +145,6 @@ all = dev = pyNeuroML[all] - airspeed>=0.5.5 pytest pytest-cov kaleido