diff --git a/docs/conf.py b/docs/conf.py index d93fa72..9786aea 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -35,6 +35,7 @@ # ones. extensions = [ 'sphinx.ext.autodoc', + 'sphinx.ext.todo', 'sphinx.ext.viewcode', ] @@ -57,6 +58,9 @@ autodoc_preserve_defaults = True +todo_include_todos = False + + default_role = 'py:obj' diff --git a/docs/examples.rst b/docs/examples.rst new file mode 100644 index 0000000..b8d9d8b --- /dev/null +++ b/docs/examples.rst @@ -0,0 +1,105 @@ +.. _examples: + +Examples +======== + +Although confidence is internally divided in a number of modules, all of the functions and types intended for use are available to import from the confidence module. + +.. code-block:: python + + # manually creating a Configuration object from a dict of values is about as + # simple as it sounds: + config = confidence.Configuration({ + 'service.host': 'example.com', + 'service.port': 443, + }) + + # suppose there's a function connect that takes two arguments + # we can connect to the configured host and port as such: + connection = connect(config.service.host, config.service.port) + # should the argument names align with the configuration (host and port), + # we could treat the configured namespace "service" as a dict and pass it as such: + connection = connect(**config.service) + +Reading configuration from a file +--------------------------------- + +Wrapping a `dict` with a `Configuration` is nice, but configuration is more often found in files. +Confidence loads configuration from file in the YAML format: + +.. code-block:: yaml + + # suppose we'd save this as path/to/file.yaml + service: + host: example.com + port: 443 + # note that we could also have expressed the two properties as + # service.host: ... + # service.port: ... + # dotted names are equivalent to nested ones + +.. code-block:: python + + # loadf simply takes a path or path-like to load configuration from + config = confidence.loadf('path/to/file.yaml') + # the result is the same as the example above, we can use config.service like we would a dict + connection = connect(**config.service) + +Reading from multiple files +--------------------------- + +If you split your configuration over multiple files as they contain configuration for different things, like a service to connect to and some local paths to store data, confidence can load them both as if they were one: + +.. code-block:: yaml + + # some system-wide configuration in /etc/paths.yaml + paths: + data: /storage/data + backup: /mnt/backup/data + +.. code-block:: yaml + + # service configuration as before, stored in path/to/service.yaml + service.host: example.com + service.port: 443 + +.. code-block:: python + + # loadf can take multiple files, the contents of which are combined into a + # single Configuration object + config = confidence.loadf('/etc/paths.yaml', 'path/to/service.yaml') + + # there's still something to connect to the service + connection = connect(**config.service) + # and some extra things that configure the place to backup to + connection.backup_to(config.paths.backup) + +Overriding defaults from one file to the next +--------------------------------------------- + +If values from multiple files overlap (like if ``/etc/paths.yaml`` would contain ``service.port: 80``), things become slightly more complicated. +Confidence uses a predictable :term:`precedence` of content here: the value that gets loaded last has the highest precedence (or 'wins'). +`loadf` will load content in the order of the arguments that get passed, so ``service.port`` would be 443, as defined in ``path/to/service.yaml``. +You can use this behaviour to define defaults somewhere, that get overridden later: + +.. code-block:: yaml + + # some system-wide configuration in /etc/paths.yaml + service.port: 80 + + paths: + data: /storage/data + backup: /mnt/backup/data + +.. code-block:: yaml + + service: + host: example.com + port: 443 + +.. todo:: + + - Configuration from a name + - Configuration from multiple names + - Configuration from reordered loaders + - Configuration from mixed / custom loaders diff --git a/docs/glossary.rst b/docs/glossary.rst new file mode 100644 index 0000000..b6f8eac --- /dev/null +++ b/docs/glossary.rst @@ -0,0 +1,23 @@ +Glossary +======== + +.. glossary:: + :sorted: + + load order + … + + loader + … + + locality + … + + namespace + … + + precedence + … + + reference + … diff --git a/docs/index.rst b/docs/index.rst index 9741869..3990726 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -1,7 +1,17 @@ Configure with confidence ========================= -``confidence`` helps loading configuration options from file into a user-friendly object at runtime. +Confidence does two things: it helps loading configuration options from file(s) and presents those options as a user-friendly object at runtime. +Inspired by the way Python's own ``pip`` reads its configuration (try ``pip config debug`` if you're not familiar with ``pip``'s configuration), +confidence uses a similarly flexible, but deterministic approach to combining information from multiple configuration files. +If that sounds awfully complicated, there's no requirement that you need to use anything that feels complicated. +See the :ref:`examples `, ranging from very simple to more complex uses of confidence' capabilities. + +As a quick overview, confidence contains the following features: + +- dict-like `Configuration` object supporting attribute access to configured values; +- customizable loading of multiple sources (files, environment variables, …) into a single object with deterministic precedence of those sources; +- the ability to make and resolve references to values or entire namespaces. Contents -------- @@ -9,12 +19,14 @@ Contents .. toctree:: :maxdepth: 2 + examples use recipes api + glossary + +.. todo:: -Indices and tables ------------------- + Integrate ``CHANGES.md`` here without having to duplicate it. -* :ref:`genindex` -* :ref:`search` +.. todolist:: diff --git a/docs/recipes.rst b/docs/recipes.rst index 82d0ff2..1b7de72 100644 --- a/docs/recipes.rst +++ b/docs/recipes.rst @@ -1,9 +1,11 @@ Recipes ======= -- option subtrees - - default - - singular vs plural -- combining with command line arguments - - command line defaults from files - - command line is highest precedence +.. todo:: + + - option subtrees + - default + - singular vs plural + - combining with command line arguments + - command line defaults from files + - command line is highest precedence diff --git a/docs/use.rst b/docs/use.rst index 87cbaf4..495e92d 100644 --- a/docs/use.rst +++ b/docs/use.rst @@ -1,9 +1,13 @@ How to use confidence ===================== -- loading files -- loading by name -- creating `.Configuration` objects manually +.. todo:: + + - loading files + - loading by name + - creating `.Configuration` objects manually + - pass values as ``**kwargs`` + - fail-fast: missing Logging ------- diff --git a/noxfile.py b/noxfile.py index e88d2e3..b2e4456 100644 --- a/noxfile.py +++ b/noxfile.py @@ -47,6 +47,15 @@ def update(session): session.run('pip-compile', '--upgrade', '--no-header', '--no-emit-index-url', '--pip-args', f'--python-version {oldest_python}', '--output-file', 'test-requirements.txt', 'test-requirements.in') +@nox.session(python=newest_python) +def docs(session): + session.install('-r', 'requirements.txt') + session.install('sphinx', 'sphinx-rtd-theme') + + # override setting to include todos here + session.run('sphinx-build', '-b', 'html', '-D', 'todo_include_todos=True', 'docs/', 'dist/docs/') + + @nox.session(python=oldest_python) def dist(session): session.install('wheel')