Metadata-Version: 2.1
Name: pysettings-validator
Version: 0.3.0
Summary: Python package to store your application settings. Validators are built-in!
Home-page: https://github.com/palazzem/pysettings
Author: Emanuele Palazzetti
Author-email: emanuele.palazzetti@gmail.com
License: BSD 3-Clause
Classifier: Development Status :: 4 - Beta
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: BSD License
Classifier: Programming Language :: Python
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.7
Classifier: Programming Language :: Python :: 3.8
Classifier: Programming Language :: Python :: 3.9
Classifier: Programming Language :: Python :: 3.10
Requires-Python: >=3.7.0
Description-Content-Type: text/markdown
License-File: LICENSE


# pysettings

[![PyPI version](https://badge.fury.io/py/pysettings-validator.svg)](https://badge.fury.io/py/pysettings-validator)
[![Linting](https://github.com/palazzem/pysettings/actions/workflows/linting.yaml/badge.svg?branch=master)](https://github.com/palazzem/pysettings/actions/workflows/linting.yaml)
[![Testing](https://github.com/palazzem/pysettings/actions/workflows/testing.yaml/badge.svg?branch=master)](https://github.com/palazzem/pysettings/actions/workflows/testing.yaml)

Pysettings is a Python package to store your application settings. Compared to some
settings managers, this package has been inspired by [Django Rest Frameworks validators][1]
where you can validate the user input beforehand.

That simplifies your code because settings don't need to be validated in your application
logic. Available features are:
* Store your application settings without using global objects.
* Extend your settings using a `BaseSettings` class. The resulting class can be validated
  using a `settings.is_valid()` method.
* Fields are represented with an `Option` field that takes `validators` as parameter.
  It's possible to set a `default` value if the option is not set by users.
* Out of the box validators: `not_null`, `is_https_url`.
* It's possible to add custom validators as functions.

[1]: https://www.django-rest-framework.org/api-guide/validators/

## Requirements

* Python 3.7+

## Getting Started

`pysettings` is available on PyPI:

```bash
$ pip install pysettings-validator
```

### Create your Settings

```python
from pysettings.base import BaseSettings
from pysettings.options import Option
from pysettings.validators import is_https_url

# Class definition
class Settings(BaseSettings):
    url = Option(validators=[is_https_url])
    description = Option()

# Use settings in your application
settings = Settings()
settings.url = "https://example.com"
settings.description = "A shiny Website!"
settings.is_valid()  # returns (True, [])
```

### Settings API

`settings` instance doesn't allow to set attributes not defined as `Option`. If you
try to set a setting that is not defined, a `OptionNotAvailable` exception is raised:

```python
class Settings(BaseSettings):
    description = Option()

# Use settings in your application
settings = Settings()
settings.url = "https://example.com"  # raise `OptionNotAvailable`
```

`is_valid()` exposes a `raise_exception=True` kwarg in case you prefer to not raise
exceptions in your code:

```python
class Settings(BaseSettings):
    url = Option(validators=[is_https_url])

# Use settings in your application
settings = Settings()
settings.url = "http://example.com"
settings.is_valid()                       # raise ConfigNotValid exception
settings.is_valid(raise_exception=False)  # return (False, [{'url': [{'is_https_url': 'The schema must be HTTPS'}]}])
```

### Create a Custom Validator

```python
# app/validators.py
from pysettings.exceptions import ValidationError

def is_a_boolean(value):
    if isinstance(value, bool):
        return True
    else:
        raise ValidationError("The value must a Boolean")

# app/settings.py
from .validators import is_a_boolean

class Settings(BaseSettings):
    dry_run = Option(validators=[is_a_boolean])
    description = Option()

# app/main.py
settings = Settings()
settings.dry_run = "Yes"
settings.description = "Dry run mode!"
settings.is_valid()  # raises ConfigNotValid exception
```

### Test your Settings (pytest)

If you need to change some of your settings during tests, you can use the following snippet
to restore the previous settings after each test:

```python
# tests/fixtures/settings.py
from pysettings.base import BaseSettings
from pysettings.options import Option
from pysettings.validators import is_https_url

# Class definition
class TestSettings(BaseSettings):
    url = Option(validators=[is_https_url])
    description = Option()

# Use settings in your application
settings = TestSettings()
settings.url = "https://example.com"
settings.description = "A shiny Website!"
settings.is_valid()

# tests/conftest.py
import copy
import pytest

from .fixtures import settings as config


@pytest.fixture
def settings():
    previous_config = copy.deepcopy(config.settings)
    yield config.settings
    config.settings = previous_config

# tests/test_settings.py
def test_settings_changes_1(settings):
    assert settings.description == "A shiny Website!"
    settings.description = "Test 1"
    assert settings.description == "Test 1"


def test_settings_changes_2(settings):
    assert settings.description == "A shiny Website!"
    settings.description = "Test 2"
    assert settings.description == "Test 2"
```

## Development

We accept external contributions even though the project is mostly designed for personal
needs. If you think some parts can be exposed with a more generic interface, feel free
to open a GitHub issue and to discuss your suggestion.

### Coding Guidelines

We use [flake8][1] as a style guide enforcement. That said, we also use [black][2] to
reformat our code, keeping a well defined style even for quotes, multi-lines blocks and other.
Before submitting your code, be sure to launch `black` to reformat your PR.

[1]: https://pypi.org/project/flake8/
[2]: https://github.com/ambv/black

### Testing

`tox` is used to execute the following test matrix:
* `lint`: launches `flake8` and `black --check` to be sure the code honors our style guideline
* `py{3.7,3.8,3.9,3.10,3.11}`: launches `py.test` to execute tests with different Python versions.

To launch the full test matrix, just:

```bash
$ tox
```
