mirror of
https://github.com/Steffo99/cfig.git
synced 2024-11-21 23:44:21 +00:00
🗒️ Keep improving the docs
This commit is contained in:
parent
8cdd6cb5b3
commit
6e473687da
2 changed files with 179 additions and 8 deletions
|
@ -8,20 +8,26 @@ The :mod:`cfig` package provides a simple but powerful configuration manager for
|
||||||
Pitch
|
Pitch
|
||||||
=====
|
=====
|
||||||
|
|
||||||
Ever had trouble making your application configurable?
|
| Ever had trouble making your application configurable?
|
||||||
Ever fought with global objects which impeded code reuse?
|
| Ever fought with global objects which impeded code reuse?
|
||||||
Ever had a confused user ask you how to configure your application?
|
| Ever had a confused user ask you how to configure your application?
|
||||||
In that case, :mod:`cfig` is for you!
|
| In that case, :mod:`cfig` is for you!
|
||||||
|
|
||||||
|
|
||||||
Minimal example
|
Minimal example
|
||||||
===============
|
===============
|
||||||
|
|
||||||
.. image:: example-definition.png
|
.. figure:: example-definition.png
|
||||||
|
|
||||||
.. image:: example-usage.png
|
An example configuration value is defined.
|
||||||
|
|
||||||
.. image:: example-configuration.png
|
.. figure:: example-usage.png
|
||||||
|
|
||||||
|
The previously defined configuration value is used in the program.
|
||||||
|
|
||||||
|
.. figure:: example-configuration.png
|
||||||
|
|
||||||
|
An user configuring the program previews the current configuration.
|
||||||
|
|
||||||
|
|
||||||
Table of contents
|
Table of contents
|
||||||
|
|
|
@ -12,16 +12,181 @@ This page describes how to use :mod:`cfig` in an application.
|
||||||
Creating a configuration module
|
Creating a configuration module
|
||||||
===============================
|
===============================
|
||||||
|
|
||||||
.. todo::
|
First, create a new ``.py`` file inside your package with the following contents:
|
||||||
|
|
||||||
|
.. code-block:: python
|
||||||
|
:emphasize-lines: 1,2,4
|
||||||
|
|
||||||
|
import cfig
|
||||||
|
import typing as t
|
||||||
|
|
||||||
|
config = cfig.Configuration()
|
||||||
|
|
||||||
|
This will:
|
||||||
|
|
||||||
|
#. Import :mod:`cfig` into your module
|
||||||
|
#. Import :mod:`typing` into your module and alias it as ``t`` for ease of use
|
||||||
|
#. Create a new :class:`~cfig.config.Configuration` with the default parameters, which will be able to be configured from `environment variables`_ and from environment files (files whose path is specified in an environment variable suffixed with ``_FILE``)
|
||||||
|
|
||||||
|
.. _environment variables: https://wiki.archlinux.org/title/Environment_variables
|
||||||
|
|
||||||
|
|
||||||
Creating configurable variables
|
Creating configurable variables
|
||||||
===============================
|
===============================
|
||||||
|
|
||||||
|
Basics
|
||||||
|
------
|
||||||
|
|
||||||
|
To make use of :mod:`cfig`, you'll need to create one or more configurable variables in your module file:
|
||||||
|
|
||||||
|
.. code-block:: python
|
||||||
|
:emphasize-lines: 6,7,8,9
|
||||||
|
|
||||||
|
import cfig
|
||||||
|
import typing as t
|
||||||
|
|
||||||
|
config = cfig.Configuration()
|
||||||
|
|
||||||
|
@config.required()
|
||||||
|
def SECRET_PASSWORD(val: str) -> str:
|
||||||
|
"""The secret password required to use this application!"""
|
||||||
|
return val
|
||||||
|
|
||||||
|
The newly added lines create a new configurable value named ``SECRET_PASSWORD``:
|
||||||
|
|
||||||
|
* the **name** of the function is used as :term:`key` of the configurable value;
|
||||||
|
* the ``@config.required()`` **decorator** marks the value as required, preventing your application from launching if it is not set;
|
||||||
|
* the **function parameters** consist of a single :class:`str` parameter named ``val``, which is the string read from the environment variable having the same name of the function;
|
||||||
|
* the **docstring** defines the meaning of the configuration value in natural language;
|
||||||
|
* the **contents of the function** are used to process the input string into more refined Python objects;
|
||||||
|
* the **return annotation** of the function is used to let IDEs know what type this configuration value will be.
|
||||||
|
|
||||||
.. todo::
|
.. todo::
|
||||||
|
|
||||||
|
Maybe say that it is called a :term:`resolver`?
|
||||||
|
|
||||||
|
|
||||||
|
Optional
|
||||||
|
--------
|
||||||
|
|
||||||
|
Configuration values can be optional:
|
||||||
|
|
||||||
|
.. code-block:: python
|
||||||
|
:emphasize-lines: 11,12,13,14,15,16
|
||||||
|
|
||||||
|
import cfig
|
||||||
|
import typing as t
|
||||||
|
|
||||||
|
config = cfig.Configuration()
|
||||||
|
|
||||||
|
@config.required()
|
||||||
|
def SECRET_PASSWORD(val: str) -> str:
|
||||||
|
"""The secret password required to use this application!"""
|
||||||
|
return val
|
||||||
|
|
||||||
|
@config.optional()
|
||||||
|
def SECRET_USERNAME(val: t.Optional[str]) -> str:
|
||||||
|
"""The username to require users to login as. If unset, defaults to `root`."""
|
||||||
|
if val is None:
|
||||||
|
return "root"
|
||||||
|
return val
|
||||||
|
|
||||||
|
Optional values differ from required ones in their decorator and signature:
|
||||||
|
|
||||||
|
#. The decorator is ``@config.optional()`` instead of ``@config.required()``;
|
||||||
|
#. Since the passed ``val`` can be :data:`None`, it is given a signature of :data:`typing.Optional`.
|
||||||
|
|
||||||
|
|
||||||
|
Processing
|
||||||
|
----------
|
||||||
|
|
||||||
|
.. todo::
|
||||||
|
|
||||||
|
A few words about value processing.
|
||||||
|
|
||||||
|
.. code-block:: python
|
||||||
|
:emphasize-lines: 18,19,20,21,22,23,24
|
||||||
|
|
||||||
|
import cfig
|
||||||
|
import typing as t
|
||||||
|
|
||||||
|
config = cfig.Configuration()
|
||||||
|
|
||||||
|
@config.required()
|
||||||
|
def SECRET_PASSWORD(val: str) -> str:
|
||||||
|
"""The secret password required to use this application!"""
|
||||||
|
return val
|
||||||
|
|
||||||
|
@config.optional()
|
||||||
|
def SECRET_USERNAME(val: t.Optional[str]) -> str:
|
||||||
|
"""The username to require users to login as. If unset, defaults to `root`."""
|
||||||
|
if val is None:
|
||||||
|
return "root"
|
||||||
|
return val
|
||||||
|
|
||||||
|
@config.required()
|
||||||
|
def MAX_USERS(val: str) -> int:
|
||||||
|
"""The maximum number of users that will be able to login to this application."""
|
||||||
|
try:
|
||||||
|
return int(val)
|
||||||
|
except ValueError:
|
||||||
|
raise cfig.InvalidValueError("Not an int.")
|
||||||
|
|
||||||
|
.. todo::
|
||||||
|
|
||||||
|
A few words about slower resolvers.
|
||||||
|
|
||||||
|
|
||||||
Adding CLI support
|
Adding CLI support
|
||||||
==================
|
==================
|
||||||
|
|
||||||
.. todo::
|
.. todo::
|
||||||
|
|
||||||
|
What is the CLI and why is it useful?
|
||||||
|
|
||||||
|
.. code-block:: python
|
||||||
|
:emphasize-lines: 26,27
|
||||||
|
|
||||||
|
import cfig
|
||||||
|
import typing as t
|
||||||
|
|
||||||
|
config = cfig.Configuration()
|
||||||
|
|
||||||
|
@config.required()
|
||||||
|
def SECRET_PASSWORD(val: str) -> str:
|
||||||
|
"""The secret password required to use this application!"""
|
||||||
|
return val
|
||||||
|
|
||||||
|
@config.optional()
|
||||||
|
def SECRET_USERNAME(val: t.Optional[str]) -> str:
|
||||||
|
"""The username to require users to login as. If unset, defaults to `root`."""
|
||||||
|
if val is None:
|
||||||
|
return "root"
|
||||||
|
return val
|
||||||
|
|
||||||
|
@config.required()
|
||||||
|
def MAX_USERS(val: str) -> int:
|
||||||
|
"""The maximum number of users that will be able to login to this application."""
|
||||||
|
try:
|
||||||
|
return int(val)
|
||||||
|
except ValueError:
|
||||||
|
raise cfig.InvalidValueError("Not an int.")
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
config.cli()
|
||||||
|
|
||||||
|
.. todo::
|
||||||
|
|
||||||
|
What will be displayed here?
|
||||||
|
|
||||||
|
|
||||||
|
Use the configuration
|
||||||
|
=====================
|
||||||
|
|
||||||
|
.. todo::
|
||||||
|
|
||||||
|
How do I use the created values in my application?
|
||||||
|
|
||||||
|
.. todo::
|
||||||
|
|
||||||
|
Why does ``is None`` not work?
|
Loading…
Reference in a new issue