Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Preparing merge of PR #20 #21

Merged
merged 48 commits into from
Feb 1, 2019
Merged
Show file tree
Hide file tree
Changes from 39 commits
Commits
Show all changes
48 commits
Select commit Hold shift + click to select a range
a95d848
Refactored strategy and loaders
hernantz Dec 26, 2018
371d076
Added motivation section to docs
hernantz Dec 26, 2018
85dac59
Simpler import
hernantz Dec 26, 2018
6fe8ad9
Removed print stmt
hernantz Dec 26, 2018
6348e08
Renamed strategies for loaders
hernantz Dec 26, 2018
43491f4
Added docs for loaders
hernantz Dec 26, 2018
95029eb
Check for required config file
hernantz Jan 5, 2019
07f57bf
Do not require the .env file
hernantz Jan 10, 2019
81d7005
Added RecursiveSearch loader as default
hernantz Jan 11, 2019
23a9aad
Changed strategy for Commandline loader
hernantz Jan 11, 2019
a15b80b
Fixed typo
hernantz Jan 11, 2019
0e99713
Formated table
hernantz Jan 12, 2019
deb2dc0
Enabled autoconf for docs
hernantz Jan 13, 2019
cd75cc5
Added autodocs for loaders
hernantz Jan 14, 2019
1fb16d9
Fixed typo
hernantz Jan 15, 2019
a5d29b8
Changed implementation on UNSET
hernantz Jan 15, 2019
8bc3542
Put whitespaces back
hernantz Jan 15, 2019
d2ac38e
Parse config files lazyly
hernantz Jan 15, 2019
19606d1
Added docstrings
hernantz Jan 15, 2019
86e19d4
Removed unused import
hernantz Jan 19, 2019
e1db3b5
Moved initialized checks
hernantz Jan 19, 2019
e9848db
Fixed typo
hernantz Jan 19, 2019
d7fafa7
Added an advanced usage page
hernantz Jan 19, 2019
e85740c
Documented CommandLine loader
hernantz Jan 21, 2019
7285599
Documented how to create a new loader
hernantz Jan 23, 2019
a8f8247
Pointed out the importance of raising KeyError
hernantz Jan 24, 2019
ce64a49
Added comparison with python-dotenv
hernantz Jan 24, 2019
e2a3409
Added note for dump-env
hernantz Jan 25, 2019
71b0cfb
Documented default behavior and RecursiveSearch loader
hernantz Jan 26, 2019
2149cf4
Made note on prettyconf's agnosticism
hernantz Jan 26, 2019
dbb81b5
Replaced InvalidConfigurationCast with the builtin TypeError exception
hernantz Jan 29, 2019
b2d98b3
Added missing InvalidPath import
hernantz Jan 29, 2019
bbd6e65
Added note about missing files
hernantz Jan 29, 2019
5e9839b
Refactored tests
hernantz Jan 29, 2019
5f3471a
Added changelog to the docs
hernantz Jan 30, 2019
93497da
:lipstick: Apply some pep-8 code formatting
osantana Jan 31, 2019
13c8f39
:lipstick: Make PyCharm happy again
osantana Jan 31, 2019
4e15a6a
Remove dead code
osantana Jan 31, 2019
32a9df9
Remove support for py2.7
osantana Jan 31, 2019
45d7319
Remove tox support
osantana Jan 31, 2019
2ed88bf
Preparing release of 2.0.0
osantana Jan 31, 2019
ccbd1b2
Remove pypy3.5 from macos matrix (it's broken at TravisCI)
osantana Jan 31, 2019
bf02c30
Remove a specific code for py2
osantana Jan 31, 2019
ee8673e
Add more tests to improve code coverage
osantana Jan 31, 2019
24e8ec9
Try to build and test prettyconf with python 3.7
osantana Jan 31, 2019
e445e87
Try to build and test prettyconf with python 3.7 in macOS
osantana Jan 31, 2019
6197d5f
Add __repr__ methods to loaders and configuration objects
osantana Jan 31, 2019
f56671f
Add more assertions in discovery test to improve code coverage
osantana Jan 31, 2019
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
20 changes: 6 additions & 14 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,36 +4,28 @@ sudo: false

matrix:
include:
- os: linux
python: 2.7
- os: linux
python: 3.4
- os: linux
python: 3.5
- 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: 3.6
- os: linux
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
osantana marked this conversation as resolved.
Show resolved Hide resolved
- PYENV_ROOT=~/.pyenv
- PATH=$PYENV_ROOT/shims:$PYENV_ROOT/bin:$PATH
- os: osx
language: generic
env:
- PYTHON_VERSION=pypy-2.6.0
- PYTHON_VERSION=pypy3.5-6.0.0
- PYENV_ROOT=~/.pyenv
- PATH=$PYENV_ROOT/shims:$PYENV_ROOT/bin:$PATH

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
37 changes: 34 additions & 3 deletions docs/source/introduction.rst
Original file line number Diff line number Diff line change
@@ -1,8 +1,39 @@
What's prettyconf
-----------------

Pretty Conf is a Python library created to make easy the separation of
configuration and code following the recomendations of `12 Factor`_'s topic
about configs.
Prettyconf is a framework agnostic python library created to make easy the
separation of configuration and code following the recomendations of `12
Factor`_'s topic about configs.


Motivation
++++++++++

Configuration is just another API of you app, aimed for users who will install
and run it, that allows them to *preset* the state of a program, without having
to interact with it, only through static files or environment variables.

It is an important aspect of the architecture of any system, yet it is
sometimes overlooked.

It is important to provide a clear separation of configuration and code. This
is because config varies substantially across deploys and executions, code
should not. The same code can be run inside a container or in a regular
machine, it can be executed in production or in testing environments.

Well designed applications allow different ways to be configured. A proper
settings-discoverability chain goes as follows:

1. First CLI args are checked.
2. Then Environment variables.
3. Config files in different directories, that also imply some hierarchy. For
example: config files in ``/etc/myapp/settings.ini`` are applied
system-wide, while ``~/.config/myapp/settings.ini`` take precedence and are
user-specific.
4. Hardcoded constants.

This raises the need to consolidate configuration in a single source of truth
to avoid having config management scattered all over the codebase.


.. _`12 Factor`: http://12factor.net/
Loading