diff --git a/efel/api.py b/efel/api.py index f7575fed..b4063d67 100644 --- a/efel/api.py +++ b/efel/api.py @@ -79,7 +79,7 @@ def set_dependency_file_location(location: str | Path) -> None: Raises: FileNotFoundError: If the path to the dependency file doesn't exist. """ - _settings.set_setting('dependencyfile_path', str(location)) + set_setting('dependencyfile_path', str(location)) def get_dependency_file_location() -> str: @@ -99,7 +99,7 @@ def set_threshold(new_threshold: float) -> None: new_threshold: The new spike detection threshold value (in the same units as the traces, e.g. mV). """ - _settings.set_setting('Threshold', new_threshold) + set_setting('Threshold', new_threshold) @deprecated("Use `set_setting('DerivativeThreshold', " @@ -114,7 +114,7 @@ def set_derivative_threshold(new_derivative_threshold: float) -> None: new_derivative_threshold: The new derivative threshold value (in the same units as the traces, e.g. mV/ms). """ - _settings.set_setting('DerivativeThreshold', new_derivative_threshold) + set_setting('DerivativeThreshold', new_derivative_threshold) def get_feature_names() -> list[str]: @@ -216,6 +216,8 @@ def _initialise() -> None: # Set the settings in the cppcore settings_attrs = vars(_settings) for setting_name, setting_value in settings_attrs.items(): + if isinstance(setting_value, bool): + setting_value = int(setting_value) if isinstance(setting_value, int): cppcore.setFeatureInt(setting_name, [setting_value]) elif isinstance(setting_value, float): @@ -230,19 +232,19 @@ def _initialise() -> None: @deprecated("Use `set_setting()` instead") def set_int_setting(setting_name: str, new_value: int) -> None: """Set a certain integer setting to a new value.""" - _settings.set_int(setting_name, new_value) + set_setting(setting_name, new_value) @deprecated("Use `set_setting()` instead") def set_double_setting(setting_name: str, new_value: float) -> None: """Set a certain double setting to a new value.""" - _settings.set_double(setting_name, new_value) + set_setting(setting_name, new_value) @deprecated("Use `set_setting()` instead") def set_str_setting(setting_name: str, new_value: str) -> None: """Set a certain string setting to a new value.""" - _settings.set_str(setting_name, new_value) + set_setting(setting_name, new_value) @overload diff --git a/efel/settings.py b/efel/settings.py index 2b98ef43..6220a9d5 100644 --- a/efel/settings.py +++ b/efel/settings.py @@ -53,7 +53,7 @@ class Settings: rise_end_perc (float): Rise end percentage (default: 1.0). initial_perc (float): Initial percentage (default: 0.1). min_spike_height (float): Minimum spike height (default: 20.0). - strict_stiminterval (int): Strict stimulus interval (default: 0). + strict_stiminterval (bool): Strict stimulus interval (default: False). initburst_freq_threshold (int): Initial burst frequency threshold (default: 50) initburst_sahp_start (int): Initial burst SAHP start (default: 5). @@ -63,7 +63,7 @@ class Settings: current_base_mode (str): Current base mode (default: "mean"). precision_threshold (float): Precision threshold (default: 1e-10). sahp_start (float): SAHP start (default: 5.0). - ignore_first_ISI (int): Ignore first ISI (default: 1). + ignore_first_ISI (bool): Ignore first ISI (default: True). impedance_max_freq (float): Impedance maximum frequency (default: 50.0). """ @@ -87,7 +87,7 @@ class Settings: rise_end_perc: float = 1.0 initial_perc: float = 0.1 min_spike_height: float = 20.0 - strict_stiminterval: int = 0 + strict_stiminterval: bool = False initburst_freq_threshold: int = 50 initburst_sahp_start: int = 5 initburst_sahp_end: int = 100 @@ -96,15 +96,17 @@ class Settings: current_base_mode: str = "mean" precision_threshold: float = 1e-10 sahp_start: float = 5.0 - ignore_first_ISI: int = 1 + ignore_first_ISI: bool = True impedance_max_freq: float = 50.0 - def set_setting(self, setting_name: str, new_value: Union[int, float, str]) -> None: + def set_setting(self, + setting_name: str, + new_value: Union[int, float, str, bool]) -> None: """Set a certain setting to a new value. Args: setting_name (str): Name of the setting to be modified. - new_value (Union[int, float, str]): New value for the setting. + new_value (Union[int, float, str, bool]): New value for the setting. Raises: ValueError: If the value is of the wrong type. @@ -114,10 +116,9 @@ def set_setting(self, setting_name: str, new_value: Union[int, float, str]) -> N if hasattr(self, setting_name): expected_types = {f.name: f.type for f in fields(self)} expected_type = expected_types.get(setting_name) - if expected_type and expected_type != type(new_value): - expected_type_name = expected_type.__name__ + if expected_type and not isinstance(new_value, expected_type): raise ValueError(f"Invalid value for setting '{setting_name}'. " - f"Expected type: {expected_type_name}.") + f"Expected type: {expected_type.__name__}.") else: logger.warning("Setting '%s' not found in settings. " "Adding it as a new setting.", setting_name) @@ -130,18 +131,6 @@ def set_setting(self, setting_name: str, new_value: Union[int, float, str]) -> N setattr(self, setting_name, new_value) - @deprecated("Use set_setting instead") - def set_int(self, setting_name: str, new_value: int) -> None: - self.set_setting(setting_name, new_value) - - @deprecated("Use set_setting instead") - def set_double(self, setting_name: str, new_value: float) -> None: - self.set_setting(setting_name, new_value) - - @deprecated("Use set_setting instead") - def set_str(self, setting_name: str, new_value: str) -> None: - self.set_setting(setting_name, new_value) - def reset_to_default(self): """Reset settings to their default values""" default_settings = Settings() diff --git a/tests/test_basic.py b/tests/test_basic.py index 01b6e3ea..d7e158e6 100644 --- a/tests/test_basic.py +++ b/tests/test_basic.py @@ -39,7 +39,6 @@ from efel.io import load_ascii_input from efel.api import get_feature_values from efel.api import set_setting -from efel.api import set_derivative_threshold from efel.api import get_distance @@ -413,7 +412,7 @@ def test_setDerivativeThreshold(): features) AP_begin_voltage_orig = feature_values[0]['AP_begin_voltage'][1] - set_derivative_threshold(5.0) + set_setting('DerivativeThreshold', 5.0) feature_values = \ get_feature_values( [trace], @@ -541,7 +540,7 @@ def test_min_AHP_indices_strict(): import efel - for strict, n_of_ahp in [(0, 17), (1, 16)]: + for strict, n_of_ahp in [(False, 17), (True, 16)]: efel.reset() set_setting('strict_stiminterval', strict) @@ -598,7 +597,7 @@ def test_strict_stiminterval(): import efel - for strict, n_of_spikes in [(0, 5), (1, 3)]: + for strict, n_of_spikes in [(False, 5), (True, 3)]: efel.reset() set_setting("strict_stiminterval", strict) @@ -681,7 +680,7 @@ def test_ISI_values_noIgnore(): features = ['ISI_values'] - set_setting("ignore_first_ISI", 0) + set_setting("ignore_first_ISI", False) feature_values = \ get_feature_values( @@ -690,7 +689,7 @@ def test_ISI_values_noIgnore(): isi_values_no_ignore = feature_values[0]['ISI_values'] efel.reset() - set_setting("ignore_first_ISI", 1) + set_setting("ignore_first_ISI", True) feature_values = \ get_feature_values( @@ -2038,7 +2037,7 @@ def test_unfinished_peak(): """basic: Test if unfinished peak doesn't break spike_count""" import efel - set_setting('strict_stiminterval', 1) + set_setting('strict_stiminterval', True) dt = 0.1 v = numpy.zeros(int(100 / dt)) - 70.0 @@ -2368,7 +2367,7 @@ def test_AP_width_between_threshold_strict(): import efel efel.reset() - set_setting('strict_stiminterval', 1) + set_setting('strict_stiminterval', True) threshold = -48.0 set_setting("Threshold", threshold) @@ -3085,7 +3084,7 @@ def test_burst_indices(): """basic: Test burst_begin_indices and burst_end_indices""" import efel efel.reset() - set_setting('ignore_first_ISI', 0) + set_setting('ignore_first_ISI', False) time, voltage = load_ascii_input(burst1_url) time, voltage = interpolate(time, voltage, 0.1) @@ -3121,7 +3120,7 @@ def test_strict_burst_mean_freq(): """basic: Test strict_burst_mean_freq""" import efel efel.reset() - set_setting('ignore_first_ISI', 0) + set_setting('ignore_first_ISI', False) time, voltage = load_ascii_input(burst1_url) time, voltage = interpolate(time, voltage, 0.1) @@ -3168,7 +3167,7 @@ def test_strict_burst_number(): """basic: Test strict_burst_number""" import efel efel.reset() - set_setting('ignore_first_ISI', 0) + set_setting('ignore_first_ISI', False) time, voltage = load_ascii_input(burst1_url) time, voltage = interpolate(time, voltage, 0.1) @@ -3216,7 +3215,7 @@ def test_strict_interburst_voltage(): """basic: Test strict_interburst_voltage""" import efel efel.reset() - set_setting('ignore_first_ISI', 0) + set_setting('ignore_first_ISI', False) time, voltage = load_ascii_input(burst1_url) time, voltage = interpolate(time, voltage, 0.1) @@ -3278,7 +3277,7 @@ def test_AP_width_spike_before_stim_start(): assert len(ap_width) == 15 - set_setting('strict_stiminterval', 1) + set_setting('strict_stiminterval', True) feature_values = \ get_feature_values( [trace], @@ -3316,7 +3315,7 @@ def test_ADP_peak_amplitude(): """basic: Test ADP_peak_amplitude""" import efel efel.reset() - set_setting('strict_stiminterval', 1) + set_setting('strict_stiminterval', True) stim_start = 250.0 stim_end = 1600.0 diff --git a/tests/test_isi.py b/tests/test_isi.py index 3af480c4..dffbfa86 100644 --- a/tests/test_isi.py +++ b/tests/test_isi.py @@ -142,7 +142,7 @@ def test_single_burst_ratio(self): assert self.feature_values["single_burst_ratio"] is None # set ignore_first_ISI=False - efel.set_setting("ignore_first_ISI", 0) + efel.set_setting("ignore_first_ISI", False) self.feature_values = get_feature_values( [self.trace], self.features, raise_warnings=False)[0] diff --git a/tests/test_settings.py b/tests/test_settings.py index 8ffe1305..ffe2545d 100644 --- a/tests/test_settings.py +++ b/tests/test_settings.py @@ -31,13 +31,10 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. """ -from pathlib import Path -import numpy - import pytest from efel.settings import Settings -from efel.api import set_setting +from efel.api import set_setting, get_settings def test_set_setting(): @@ -51,24 +48,16 @@ def test_set_setting_invalid_type(): """Test that the set_setting method raises a ValueError when given an invalid type.""" settings = Settings() - try: + with pytest.raises(ValueError): settings.set_setting("Threshold", "-30.0") - except ValueError: - assert True - else: - assert False def test_set_setting_dependencyfile_path_not_found(): """Test that the set_setting method raises a FileNotFoundError when given a nonexistent file.""" settings = Settings() - try: + with pytest.raises(FileNotFoundError): settings.set_setting("dependencyfile_path", "nonexistent_file.txt") - except FileNotFoundError: - assert True - else: - assert False def test_reset_to_default(): @@ -78,3 +67,45 @@ def test_reset_to_default(): settings.Threshold = -30.0 settings.reset_to_default() assert settings.Threshold == -20.0 + + +def test_get_settings(): + """Test that the get_settings method returns an instance of efel.Settings.""" + settings = get_settings() + assert isinstance(settings, Settings) + + +def test_str_method(): + """Test that the __str__ method returns the correct string representation.""" + settings = Settings() + expected_output = ( + "Threshold: -20.0\n" + "DerivativeThreshold: 10.0\n" + "DownDerivativeThreshold: -12.0\n" + f"dependencyfile_path: {settings.dependencyfile_path}\n" + "spike_skipf: 0.1\n" + "max_spike_skip: 2\n" + "interp_step: 0.1\n" + "burst_factor: 1.5\n" + "strict_burst_factor: 2.0\n" + "voltage_base_start_perc: 0.9\n" + "voltage_base_end_perc: 1.0\n" + "current_base_start_perc: 0.9\n" + "current_base_end_perc: 1.0\n" + "rise_start_perc: 0.0\n" + "rise_end_perc: 1.0\n" + "initial_perc: 0.1\n" + "min_spike_height: 20.0\n" + "strict_stiminterval: False\n" + "initburst_freq_threshold: 50\n" + "initburst_sahp_start: 5\n" + "initburst_sahp_end: 100\n" + "DerivativeWindow: 3\n" + "voltage_base_mode: mean\n" + "current_base_mode: mean\n" + "precision_threshold: 1e-10\n" + "sahp_start: 5.0\n" + "ignore_first_ISI: True\n" + "impedance_max_freq: 50.0" + ) + assert str(settings) == expected_output