diff --git a/brian2/core/preferences.py b/brian2/core/preferences.py index 4c5e34a92..db0288b69 100644 --- a/brian2/core/preferences.py +++ b/brian2/core/preferences.py @@ -11,7 +11,13 @@ from brian2.units.fundamentalunits import Quantity, have_same_dimensions from brian2.utils.stringtools import deindent, indent -__all__ = ["PreferenceError", "BrianPreference", "prefs", "brian_prefs"] +__all__ = [ + "PreferenceError", + "BrianPreference", + "prefs", + "brian_prefs", + "brian_configuration", +] def parse_preference_name(name): @@ -304,7 +310,7 @@ def _restore(self): """ self.prefs.update(**self.backup_prefs) - def _get_one_documentation(self, basename, link_targets): + def _get_one_documentation(self, basename, valuefunc, link_targets): """ Document a single category of preferences. """ @@ -323,7 +329,7 @@ def _get_one_documentation(self, basename, link_targets): if link_targets: # Make a link target s += f".. _brian-pref-{linkname}:\n\n" - s += f"``{name}`` = ``{pref.representor(pref.default)}``\n" + s += f"``{name}`` = ``{pref.representor(valuefunc(pref, name))}``\n" s += indent(deindent(pref.docs, docstring=True)) s += "\n\n" return s @@ -345,13 +351,45 @@ def get_documentation(self, basename=None, link_targets=True): s += basename + "\n" + '"' * len(basename) + "\n\n" else: s += "**" + basename + "**\n\n" - s += self._get_one_documentation(basename, link_targets) + s += self._get_one_documentation( + basename, lambda pref, fullname: pref.default, link_targets + ) # for basename in self.pref_register: # s += '**' + basename + '**\n\n' # s += basename+'\n'+'"'*len(basename)+'\n\n' # s += self._get_one_documentation(basename, link_targets) else: - s += self._get_one_documentation(basename, link_targets) + s += self._get_one_documentation( + basename, lambda pref, fullname: pref.default, link_targets + ) + + return s + + def get_configuration(self, basename=None): + """ + Generates a string documenting the current values of all preferences + with the given `basename`. If no `basename` is given, all preferences + are documented. + """ + s = "" + if basename is None: + basenames = sorted( + [tuple(basename.split(".")) for basename in self.pref_register] + ) + for basename in basenames: + lev = len(basename) + basename = ".".join(basename) + if lev == 1: + s += basename + "\n" + '"' * len(basename) + "\n\n" + else: + s += "**" + basename + "**\n\n" + s += self._get_one_documentation( + basename, lambda pref, fullname: self[fullname], False + ) + else: + s += self._get_one_documentation( + basename, lambda pref, fullname: self[fullname], False + ) return s @@ -741,3 +779,10 @@ def __getitem__(self, item): brian_prefs = ErrorRaiser() + + +def brian_configuration(): + """ + Return a string summarizing the current Brian preferences. + """ + return prefs.get_configuration() diff --git a/brian2/only.py b/brian2/only.py index fb0f2d80b..5308e30e3 100644 --- a/brian2/only.py +++ b/brian2/only.py @@ -96,6 +96,7 @@ def restore_initial_state(): "BrianPreference", "prefs", "brian_prefs", + "brian_configuration", "Clock", "EventClock", "defaultclock", diff --git a/brian2/tests/test_preferences.py b/brian2/tests/test_preferences.py index 45c17c74d..818eb6a92 100644 --- a/brian2/tests/test_preferences.py +++ b/brian2/tests/test_preferences.py @@ -4,7 +4,7 @@ from numpy import float32, float64 from numpy.testing import assert_equal -from brian2 import amp, restore_initial_state, volt +from brian2 import amp, brian_configuration, prefs, restore_initial_state, volt from brian2.core.preferences import ( BrianGlobalPreferences, BrianGlobalPreferencesView, @@ -198,6 +198,45 @@ def test_brianglobalpreferences(): assert gp["a.b"] == 5 +@pytest.mark.codegen_independent +def test_brian_configuration(): + configuration = brian_configuration() + assert len(configuration) + assert "core" in configuration + assert "``core.default_float_dtype``" in configuration + + gp = BrianGlobalPreferences() + gp.register_preferences( + "example", + "Example preferences", + value=BrianPreference(1, "Example value"), + ) + example_configuration = gp.get_configuration() + assert "example" in example_configuration + assert "``example.value`` = ``1``" in example_configuration + + +@pytest.mark.codegen_independent +def test_brian_configuration_current_values(): + original_configuration = brian_configuration() + try: + prefs["core.default_float_dtype"] = float32 + changed_configuration = brian_configuration() + assert changed_configuration != original_configuration + assert "``core.default_float_dtype`` = ``float32``" in changed_configuration + + prefs.reset_to_defaults() + reset_configuration = brian_configuration() + assert reset_configuration == original_configuration + finally: + restore_initial_state() + + +@pytest.mark.codegen_independent +def test_brian_configuration_importable(): + assert callable(brian_configuration) + + @pytest.mark.codegen_independent def test_preference_name_access(): """ diff --git a/docs_sphinx/advanced/preferences.rst b/docs_sphinx/advanced/preferences.rst index aca48dc42..b12e86af3 100644 --- a/docs_sphinx/advanced/preferences.rst +++ b/docs_sphinx/advanced/preferences.rst @@ -19,6 +19,12 @@ work, e.g. in ipython, as it offers autocompletion and documentation. In ipython, ``prefs.codegen.cpp?`` would display a docstring with all the preferences available in the ``codegen.cpp`` category. +To get a plain-text summary of the current preference values, use +`brian_configuration`:: + + >>> from brian2 import brian_configuration + >>> print(brian_configuration()) # doctest: +SKIP + Preference files ----------------