Skip to content

Commit

Permalink
Documentation updated (#34)
Browse files Browse the repository at this point in the history
  • Loading branch information
pilosus committed Jun 27, 2019
1 parent 410d2e4 commit 2281967
Show file tree
Hide file tree
Showing 6 changed files with 172 additions and 9 deletions.
3 changes: 2 additions & 1 deletion CHANGELOG.rst
@@ -1,9 +1,10 @@
Changelog
---------

v0.6.0 (unreleased)
v0.6.0 (2019-06-27)
...................
* Add CLI utility (#35) by @pilosus
* Update documentation, add integration examples (#34) by @pilosus

v0.5.2 (2019-06-17)
...................
Expand Down
1 change: 0 additions & 1 deletion docs/code/aiohttp_integration.py

This file was deleted.

104 changes: 103 additions & 1 deletion docs/code/flask_integration.py
@@ -1 +1,103 @@
# coming soon
from flask import Flask
from flask.logging import default_handler
from piny import YamlLoader, StrictMatcher, PydanticValidator
from pydantic import BaseModel, validator
from typing import Any, Dict, Optional
from werkzeug.serving import run_simple
import logging
import sys

#
# Validation
#


class AppSettings(BaseModel):
company: str
secret: str
max_content_len: Optional[int] = None
debug: bool = False
testing: bool = False


class LoggingSettings(BaseModel):
fmt: str
date_fmt: str
level: str

@validator("level")
def validate_name(cls, value):
upper = value.upper()
if upper not in logging._nameToLevel:
raise ValueError("Invalid logging level")
return upper


class Configuration(BaseModel):
app: AppSettings
logging: LoggingSettings


#
# Helpers
#


def configure_app(app: Flask, configuration: Dict[str, Any]) -> None:
"""
Apply configs to application
"""
app.settings = configuration
app.secret_key = app.settings["app"]["secret"].encode("utf-8")


def configure_logging(app: Flask) -> None:
"""
Configure app's logging
"""
app.logger.removeHandler(default_handler)
log_formatter = logging.Formatter(
fmt=app.settings["logging"]["fmt"], datefmt=app.settings["logging"]["date_fmt"]
)
log_handler = logging.StreamHandler()
log_handler.setFormatter(log_formatter)
log_handler.setLevel(app.settings["logging"]["level"])
app.logger.addHandler(log_handler)

#
# Factory
#


def create_app(path: str) -> Flask:
"""
Application factory
"""
# Get and validate config
config = YamlLoader(
path=path,
matcher=StrictMatcher,
validator=PydanticValidator,
schema=Configuration,
).load()

# Initialize app
app = Flask(__name__)

# Configure app
configure_app(app, config)
configure_logging(app)

return app


if __name__ == '__main__':
app = create_app(sys.argv[1])

@app.route("/")
def hello():
return "Hello World!"

# Run application:
# $ python flask_integration.py your-config.yaml
run_simple(hostname="localhost", port=5000, application=app)
11 changes: 10 additions & 1 deletion docs/install.rst
@@ -1,6 +1,9 @@
Installation
============

pip
---

Just use::

pip install -U piny
Expand All @@ -17,9 +20,15 @@ The full list of extra validation libraries is the following:
- ``pydantic``
- ``trafaret``

You can also clone *Piny* from GitHub and install it using ``make install``

GitHub
------

You can also clone *Piny* from `GitHub`_ and install it using ``make install``
(see :ref:`contributing-docs`)::

git clone https://github.com/pilosus/piny
cd piny
make install

.. _GitHub: https://github.com/pilosus/piny
59 changes: 54 additions & 5 deletions docs/integration.rst
Expand Up @@ -4,16 +4,65 @@ Integration Examples
Flask
-----

.. literalinclude:: code/flask_integration.py
`Flask`_ is a microframework for Python web applications. It's flexible and extensible.
Although there are best practices and traditions, Flask doesn't really enforce
the only one way to do it.

If you are working on a small project the chances are that you are using some Flask
extensions like `Flask-Mail`_ or `Flask-WTF`_. The extensions of the past are often
got configured through environment variables only. It makes the use of *Piny* cumbersome.
In mid-sized and large Flask projects though, you usually avoid using extra dependencies
whenever possible. In such a case you can fit your code to use *Piny* pretty easy.

Here is an example of a simple Flask application. Configuration file is loaded with *Piny*
and validated with *Pydantic*.

aiohttp
-------
.. literalinclude:: code/flask_integration.py

.. literalinclude:: code/aiohttp_integration.py
You can use the same pattern with application factory in other frameworks,
like `aiohttp`_ or `sanic`_.

.. _Flask: http://flask.pocoo.org/docs/1.0/
.. _Flask-Mail: https://pythonhosted.org/Flask-Mail/
.. _Flask-WTF: https://flask-wtf.readthedocs.io/en/stable/
.. _aiohttp: https://aiohttp.readthedocs.io/en/stable/
.. _sanic: https://sanic.readthedocs.io/en/latest/


Command line
------------

TODO
There are many possible applications for *Piny* CLI utility.
For example, you can use it for `Kubernetes deployment automation`_
in your CI/CD pipeline.

Piny command line tool works both with standard input/output and files.


Standard input and output
.........................

.. code-block:: bash
$ export PASSWORD=mySecretPassword
$ echo "db: \${PASSWORD}" | piny
db: mySecretPassword
Files
.....


.. code-block:: bash
$ piny config.template config.yaml
Or you can substitute environment variables in place:

.. code-block:: bash
$ piny production.yaml production.yaml
.. _Kubernetes deployment automation: https://www.digitalocean.com/community/tutorials/how-to-automate-deployments-to-digitalocean-kubernetes-with-circleci
3 changes: 3 additions & 0 deletions docs/requirements.txt
@@ -1,3 +1,6 @@
Sphinx==2.1.1
sphinx-rtd-theme==0.4.3
sphinx-click==2.2.0

# Inregration examples
Flask==1.0.3

0 comments on commit 2281967

Please sign in to comment.