From 4a69d3a2bab6fd393ebe0d2acd32d2cc48d092eb Mon Sep 17 00:00:00 2001 From: Stefano Pigozzi Date: Fri, 22 Apr 2022 18:21:53 +0200 Subject: [PATCH] :spiral_notepad: Complete the documentation! --- cfig/config.py | 6 +-- docs/advanced.rst | 109 ++++++++++++++++++++++++++++++++++++++++++++ docs/conf.py | 6 +++ docs/index.rst | 1 + docs/quickstart.rst | 33 ++++++++++++++ 5 files changed, 152 insertions(+), 3 deletions(-) create mode 100644 docs/advanced.rst diff --git a/cfig/config.py b/cfig/config.py index 360dfb5..0062f97 100644 --- a/cfig/config.py +++ b/cfig/config.py @@ -21,7 +21,7 @@ class Configuration: A collection of proxies with methods to easily define more. """ - DEFAULT_SOURCES = [ + DEFAULT_SOURCES: list[Source] = [ EnvironmentSource(), EnvironmentFileSource(), ] @@ -76,14 +76,14 @@ class Configuration: log.debug(f"Unresolving: {item!r}") del item.__wrapped__ - def __init__(self, *, sources: t.Optional[t.Collection[Source]] = None): + def __init__(self, *, sources: t.Optional[list[Source]] = None): """ Create a new :class:`Configuration`. """ log.debug(f"Initializing a new {self.__class__.__qualname__} object...") - self.sources: t.Collection[Source] = sources or self.DEFAULT_SOURCES + self.sources: list[Source] = sources or self.DEFAULT_SOURCES """ Collection of sources to use for values of this configuration. """ diff --git a/docs/advanced.rst b/docs/advanced.rst new file mode 100644 index 0000000..c2a48d3 --- /dev/null +++ b/docs/advanced.rst @@ -0,0 +1,109 @@ +############## +Advanced usage +############## + +This page describes some more advanced :mod:`cfig` features that you might be interested in using. + + +Fail-fast +========= + +If your variables are very slow to be resolved, you may want for the :meth:`~cfig.config.Configuration.ProxyDict.resolve` method to raise as soon as a single value fails to resolve. + +For that purpose, the :meth:`~cfig.config.Configuration.ProxyDict.resolve_failfast` method is provided: + +.. code-block:: python + :emphasize-lines: 4 + + from .mydefinitionmodule import config + + if __name__ == "__main__": + config.proxies.resolve_failfast() + +Please note that the :meth:`~cfig.config.Configuration.ProxyDict.resolve_failfast` method does not raise :exc:`~cfig.errors.BatchResolutionFailure`, but raises the first occurring error instead, so you might want to catch it in this way: + +.. code-block:: python + :emphasize-lines: 4,5,6,7 + + from .mydefinitionmodule import config + + if __name__ == "__main__": + try: + config.proxies.resolve_failfast() + except cfig.ConfigurationError as err: + ... + + +Reloading variables +=================== + +You might want for the configuration to be reloaded without restarting your application. + +In that case, you may use the :meth:`~cfig.config.Configuration.ProxyDict.unresolve` method to clear the cached values, and then call :meth:`~cfig.config.Configuration.ProxyDict.resolve` again. + +.. code-block:: python + :emphasize-lines: 2,3 + + ... + config.proxies.unresolve() + config.proxies.resolve() + ... + +To reload a single variable, you may use the ``del`` keyword: + +.. code-block:: python + :emphasize-lines: 2 + + ... + del MY_VARIABLE.__wrapped__ + ... + + +Sources selection +================= + +If you need further fine-tuning of the places to gather configuration values from, you may specify them via the :attr:`cfig.config.Configuration.sources` collection: + +.. code-block:: python + :emphasize-lines: 2,3,5,6,7,8,9,10 + + import cfig + import cfig.sources.env + import cfig.sources.envfile + + config = cfig.Configuration(sources=[ + cfig.source.env.EnvironmentSource(), + cfig.source.env.EnvironmentSource(prefix="PROD_"), + cfig.source.envfile.EnvironmentFileSource(), + cfig.source.envfile.EnvironmentFileSource(suffix="_PATH"), + ]) + +The specified sources are used in the order they are specified. + +They may also be altered at runtime, if for some *crazy reason* you need that feature: + +.. code-block:: python + :emphasize-lines: 6,7,8 + + import cfig + import cfig.sources.env + + config = cfig.Configuration() + + config.sources.append( + cfig.source.env.EnvironmentSource() + ) + +.. note:: + + Already cached variables **won't** be automatically reloaded after changing the sources! + + +Sources customization +--------------------- + +If the provided sources aren't enough, you may create a custom class inheriting from :class:`~cfig.sources.base.Source`. + +.. hint:: + + Since :mod:`cfig.sources` is a namespace package, if you intend to distribute your custom source, you may want to do it by extending the namespace, for an easier developer workflow. diff --git a/docs/conf.py b/docs/conf.py index ef7dcb5..40a8fbe 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -29,6 +29,12 @@ html_theme_options = { # Set this to the main color of your project "style_nav_header_background": "#b72f37", } +html_context = { + "display_github": True, + "github_user": "Steffo99", + "github_repo": "cfig", + "github_version": "main/docs/", +} ########################## diff --git a/docs/index.rst b/docs/index.rst index 1dd43e1..45ddca0 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -38,6 +38,7 @@ Table of contents goals installation quickstart + advanced reference diff --git a/docs/quickstart.rst b/docs/quickstart.rst index d4226f9..7895deb 100644 --- a/docs/quickstart.rst +++ b/docs/quickstart.rst @@ -232,3 +232,36 @@ In the modules of your application, you can import and use the variables directl assert ALWAYS_NONE is not None assert ALWAYS_NONE == None + + +Validate all variables at once +============================== + +For a better user experience, you might want to ensure that all variables are correctly configured when your application is started. + +For that goal, :mod:`cfig` provides the :meth:`~cfig.config.Configuration.ProxyDict.resolve` method, which immediately tries to resolve and cache all configurable values defined in the :class:`~cfig.config.Configuration`: + +.. code-block:: python + :emphasize-lines: 1,4 + + from .mydefinitionmodule import config + + if __name__ == "__main__": + config.proxies.resolve() + +The method will gather all errors occurring during the resolution, and will raise all of them at once with a :exc:`~cfig.errors.BatchResolutionFailure`, which you may want to handle in a custom way: + +.. code-block:: python + :emphasize-lines: 4,5,6,7 + + from .mydefinitionmodule import config + + if __name__ == "__main__": + try: + config.proxies.resolve() + except cfig.BatchResolutionFailure as failure: + ... + +And that's it! You're using :mod:`cfig` in the best way possible :) + +See :doc:`advanced` for more features that may be useful in specific cases. \ No newline at end of file