Skip to content

Commit

Permalink
Refactor strategy and loaders and add support for cli arguments
Browse files Browse the repository at this point in the history
* Refactor strategy and loaders implementations
* Add support for CLI arguments
* Dropped support for Python 2
* Doc: Add motivation section
* Doc: Add documentation for loaders
* Doc: Add documentation for CommandLine loader
* Doc: Add documentation about how to create a new loader
* Doc: Add comparison with python-dotenv
* Doc: Add documentation explaining the default behavior and RecursiveSearch loader
* Doc: Made a note on prettyconf's framework agnosticism
* Doc: Add Changelog to the documentation
  • Loading branch information
osantana committed Feb 1, 2019
1 parent 3ec45b6 commit 3eb3e5d
Show file tree
Hide file tree
Showing 34 changed files with 945 additions and 453 deletions.
2 changes: 1 addition & 1 deletion .ci/pre.linux.sh
Original file line number Diff line number Diff line change
@@ -1 +1 @@
echo "There's no need to execute anything before running"
echo "There's no need to execute anything before running"
2 changes: 1 addition & 1 deletion .ci/pre.osx.sh
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
echo "Use pyenv rehash before executing tests"
pyenv rehash
pyenv rehash
22 changes: 9 additions & 13 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,38 +5,34 @@ sudo: false
matrix:
include:
- os: linux
python: 2.7
python: 3.5
- os: linux
python: 3.4
python: 3.6
- os: linux
python: 3.5
dist: xenial
python: 3.7
- os: linux
python: pypy
- os: osx
language: generic
env:
- PYTHON_VERSION=2.7.10
- PYENV_ROOT=~/.pyenv
- PATH=$PYENV_ROOT/shims:$PYENV_ROOT/bin:$PATH
python: pypy3
- os: osx
language: generic
env:
- PYTHON_VERSION=3.4.3
- PYTHON_VERSION=3.5.6
- PYENV_ROOT=~/.pyenv
- PATH=$PYENV_ROOT/shims:$PYENV_ROOT/bin:$PATH
- os: osx
language: generic
env:
- PYTHON_VERSION=3.5.0
- PYTHON_VERSION=3.6.8
- PYENV_ROOT=~/.pyenv
- PATH=$PYENV_ROOT/shims:$PYENV_ROOT/bin:$PATH
- os: osx
language: generic
env:
- PYTHON_VERSION=pypy-2.6.0
- PYTHON_VERSION=3.7.2
- PYENV_ROOT=~/.pyenv
- PATH=$PYENV_ROOT/shims:$PYENV_ROOT/bin:$PATH


before_install:
- bash .ci/deps.${TRAVIS_OS_NAME}.sh

Expand Down
8 changes: 8 additions & 0 deletions CHANGES.txt
Original file line number Diff line number Diff line change
@@ -1,3 +1,11 @@
2.0.0
=====

- Refactor strategy to find configuration files (thanks to @hernantz)
- Lots of improvements on documentation
- Dropped support for py2
- Dropped tox support

1.2.3
=====

Expand Down
174 changes: 174 additions & 0 deletions docs/source/advanced.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,174 @@
Advanced Usage
--------------


Most of the time you can use the ``prettyconf.config`` function to get your
settings and use the ``prettyconf``'s standard behaviour. But some times
you need to change this behaviour.

To make this changes possible you can always create your own
``Configuration()`` instance and change it's default behaviour:

.. code-block:: python
from prettyconf import Configuration
config = Configuration()
.. warning:: ``prettyconf`` will skip configuration files inside ``.zip``,
``.egg`` or wheel packages.


.. _discovery-customization:

Customizing the configuration discovery
+++++++++++++++++++++++++++++++++++++++

By default the library will use the envrionment and the directory of the file
where ``config()`` was called as the start directory to look for a ``.env``
configuration file. Consider the following file structure:

.. code-block:: text
project/
app/
.env
config.ini
settings.py
If you call ``config()`` from ``project/app/settings.py`` the library will
inspect the envrionment and then look for configuration files at
``project/app``.

You can change that behaviour, by customizing configuration loaders to look at
a different ``path`` when instantiating your ``Configuration()``:

.. code-block:: python
# Code example in project/app/settings.py
import os
from prettyconf import Configuration
from prettyconf.loaders import Environment, EnvFile
project_path = os.path.realpath(os.path.join(os.path.dirname(__file__), '..'))
env_file = f"{project_path}/.env"
config = Configuration(loaders=[Environment(), EnvFile(filename=env_file)])
The example above will start looking for configuration in the environment and
then in a ``.env`` file at ``project/`` instead of ``project/app``.

Because ``config`` is nothing but an already instantiated ``Configuration`` object,
you can also alter this ``loaders`` attribute in ``prettyconf.config`` before use it:

.. code-block:: python
# Code example in project/app/settings.py
import os
from prettyconf import config
from prettyconf.loaders import Environment, EnvFile
project_path = os.path.realpath(os.path.join(os.path.dirname(__file__), '..'))
env_file = f"{project_path}/.env"
config.loaders = [Environment(), EnvFile(filename=env_file)]
Read more about how loaders can be configured in the :doc:`loaders section<loaders>`.

.. _variable-naming:

Naming conventions for variables
++++++++++++++++++++++++++++++++

There happen to be some formating conventions for configuration paramenters
based on where they are set. For example, it is common to name environment
variables in uppercase:

.. code-block:: sh
$ DEBUG=yes OTHER_CONFIG=10 ./app.py
but if you were to set this config in an ``.ini`` file, it should probably be
in lower case:

.. code-block:: ini
[settings]
debug=yes
other_config=10
command line argments have yet another conventions:

.. code-block:: sh
$ ./app.py --debug=yes --another-config=10
Prettyconf let's you follow these aesthetics patterns by setting a
``var_format`` function when instantiating the :doc:`loaders<loaders>`.

By default, the :py:class:`Environment<prettyconf.loaders.Environment>` is
instantiated with ``var_format=str.upper`` so that lookups play nice with the
env variables.

.. code-block:: python
from prettyconf import Configuration
from prettyconf.loaders import Environment
config = Configuration(loaders=[Environment(var_format=str.upper)])
debug = config('debug', default=False, cast=config.boolean) # lookups for DEBUG=[yes|no]
Writing your own loader
+++++++++++++++++++++++

If you need a custom loader, you should just extend the :py:class:`AbstractConfigurationLoader<prettyconf.loaders.AbstractConfigurationLoader>`.

.. autoclass:: prettyconf.loaders.AbstractConfigurationLoader

For example, say you want to write a Yaml loader. It is important to note
that by raising a ``KeyError`` exception from the loader, prettyconf knows
that it has to keep looking down the loaders chain for a specific config.

.. code-block:: python
import yaml
from prettyconf.loaders import AbstractConfigurationLoader
class YamlFile(AbstractConfigurationLoader):
def __init__(self, filename):
self.filename = filename
self.config = None
def _parse(self):
if self.config is not None:
return
with open(self.filename, 'r') as f:
self.config = yaml.load(f)
def __contains__(self, item):
try:
self._parse()
except:
return False
return item in self.config
def __getitem__(self, item):
try:
self._parse()
except:
# KeyError tells prettyconf to keep looking elsewhere!
raise KeyError("{!r}".format(item))
return self.config[item]
Then configure prettyconf to use it.

.. code-block:: python
from prettyconf import config
config.loaders = [YamlFile('config.yml')]
10 changes: 10 additions & 0 deletions docs/source/changelog.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
Changelog
---------

All notable changes to this project will be documented in this file.

This project adheres to `Semantic Versioning`_.

.. _`Semantic Versioning`: https://semver.org/spec/v2.0.0.html

.. include:: ../../CHANGES.txt
7 changes: 5 additions & 2 deletions docs/source/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
# If extensions (or modules to document with autodoc) are in another directory,
# add these directories to sys.path here. If the directory is relative to the
# documentation root, use os.path.abspath to make it absolute, like shown here.
#sys.path.insert(0, os.path.abspath('.'))
sys.path.insert(0, os.path.abspath('../..'))

# -- General configuration ------------------------------------------------

Expand All @@ -32,7 +32,10 @@
# Add any Sphinx extension module names here, as strings. They can be
# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
# ones.
extensions = []
extensions = ['sphinx.ext.autodoc']

# Both the class’ and the __init__ method’s docstring are concatenated and inserted.
autoclass_content = 'both'

# Add any paths that contain templates here, relative to this directory.
templates_path = ['_templates']
Expand Down
49 changes: 36 additions & 13 deletions docs/source/faq.rst
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,16 @@ But this code have some issues:
file that will be used if `DEBUG` *envvar* is not defined.


Is prettyconf tied to Django_ or Flask_?
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

No, prettyconf was designed to be framework agnostic, be it for the web or cli
applications.

.. _`Django`: https://www.djangoproject.com/
.. _`Flask`: http://flask.pocoo.org/


What is the difference between prettyconf and python-decouple_?
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Expand All @@ -35,6 +45,11 @@ highly inspired in ``python-decouple`` and provides almost the same API.
The implementation of ``prettyconf`` is more extensible and flexible to make
behaviour configurations easier.

You can use any of them. Both are good libraries and provides a similar set of
features.

.. _`python-decouple`: https://github.com/henriquebastos/python-decouple


Why you created a library similar to python-decouple instead of use it?
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Expand All @@ -45,25 +60,33 @@ incompatible, so, it could break software that relies on the old behaviour.
Besides that it's hard to make this change on ``python-decouple`` due to
the way it's implemented.

See the lookup order of configurations below::
See the lookup order of configurations below

+---------------+-----------------+------------------------+-------------------------+
| Lookup Order | prettyconf | python-decouple (<3.0) | python-decouple (>=3.0) |
+---------------+-----------------+------------------------+-------------------------+
| 1 | ENVVAR | .env | ENVVAR |
| 2 | .env | settings.ini | .env |
| 3 | *.cfg or *.ini | ENVVAR | settings.ini |
+---------------+-----------------+------------------------+-------------------------+
+---------------+------------------+------------------------+-------------------------+
| Lookup Order | prettyconf | python-decouple (<3.0) | python-decouple (>=3.0) |
+===============+==================+========================+=========================+
| 1 | ENVVAR | .env | ENVVAR |
+---------------+------------------+------------------------+-------------------------+
| 2 | .env | settings.ini | .env |
+---------------+------------------+------------------------+-------------------------+
| 3 | \*.cfg or \*.ini | ENVVAR | settings.ini |
+---------------+------------------+------------------------+-------------------------+

.. _some: https://github.com/henriquebastos/python-decouple/pull/4
.. _contributions: https://github.com/henriquebastos/python-decouple/pull/5


Why use ``prettyconf`` instead of ``python-decouple``?
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
How does prettyconf compare to python-dotenv?
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

You can use any of them. Both are good libraries and provides a similar set of
features.
python-dotenv_ reads the key, value pair from .env file and adds them to
environment variable. It is good for some tools that simply proxy the env to
some other process, like docker-compose_ or pipenv_.

On the other hand, prettyconf does not populate the ``os.environ`` dictionary,
because it is designed to discover configuration from diferent sources, the
environment being just one of them.

.. _`python-decouple`: https://github.com/henriquebastos/python-decouple
.. _`python-dotenv`: https://github.com/theskumar/python-dotenv
.. _`pipenv`: https://pipenv.readthedocs.io/en/latest/advanced/#automatic-loading-of-env
.. _`docker-compose`: https://docs.docker.com/compose/env-file/
3 changes: 3 additions & 0 deletions docs/source/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,10 @@ Contents:
introduction.rst
installation.rst
usage.rst
advanced.rst
loaders.rst
faq.rst
changelog.rst


Indices and tables
Expand Down

0 comments on commit 3eb3e5d

Please sign in to comment.