Skip to content

Commit

Permalink
feat: add yaml source support (#51)
Browse files Browse the repository at this point in the history
  • Loading branch information
zifeo authored Jul 31, 2022
1 parent 69e9cab commit b3a0cb8
Show file tree
Hide file tree
Showing 10 changed files with 179 additions and 251 deletions.
4 changes: 2 additions & 2 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,15 @@ repos:
hooks:
- id: zimports
- repo: https://github.com/psf/black
rev: 22.3.0
rev: 22.6.0
hooks:
- id: black
- repo: https://gitlab.com/pycqa/flake8
rev: 3.9.2
hooks:
- id: flake8
- repo: https://github.com/commitizen-tools/commitizen
rev: v2.27.1
rev: v2.29.2
hooks:
- id: commitizen
stages: [commit-msg]
20 changes: 13 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@
[![Actions Status](https://github.com/zifeo/dataconf/workflows/CI/badge.svg)](https://github.com/zifeo/dataconf/actions)
[![PyPI version](https://badge.fury.io/py/dataconf.svg)](https://badge.fury.io/py/dataconf)

Simple dataclasses configuration management for Python with hocon/json/yaml/properties/env-vars/dict/cli support, based on awesome and lightweight [pyhocon](https://github.com/chimpler/pyhocon) parsing library.
Simple dataclasses configuration management for Python with
hocon/json/yaml/properties/env-vars/dict/cli support.

## Getting started

Expand Down Expand Up @@ -135,24 +136,27 @@ print(
import dataconf

conf = dataconf.string('{ name: Test }', Config)
conf = dataconf.string('name:\n\tvalue: Test', Config, loader=dataconf.YAML) # dataconf.HOCON by default
conf = dataconf.env('PREFIX_', Config)
conf = dataconf.dict({'name': 'Test'}, Config)
conf = dataconf.url('https://raw.githubusercontent.com/zifeo/dataconf/master/confs/test.hocon', Config)
conf = dataconf.file('confs/test.{hocon,json,yaml,properties}', Config)
conf = dataconf.url('https://raw.githubusercontent.com/zifeo/dataconf/master/confs/test.hocon', Config) # hocon, json, yaml, properties
conf = dataconf.file('confs/test.hocon', Config) # hocon, json, yaml, properties
conf = dataconf.cli(sys.argv, Config)

# Aggregation
conf = dataconf.multi.string(...).env(...).url(...).file(...).dict(...).cli(...).on(Config)

# Same api as Python json/yaml packages (e.g. `load`, `loads`, `dump`, `dumps`)
conf = dataconf.load('confs/test.{hocon,json,yaml,properties}', Config)
conf = dataconf.load('confs/test.hocon', Config) # hocon, json, yaml, properties
conf = dataconf.load('confs/test.yaml', Config, loader=dataconf.YAML) # dataconf.HOCON by default
dataconf.dump('confs/test.hocon', conf, out='hocon')
dataconf.dump('confs/test.json', conf, out='json')
dataconf.dump('confs/test.yaml', conf, out='yaml')
dataconf.dump('confs/test.properties', conf, out='properties')
```

For full HOCON capabilities see [here](https://github.com/chimpler/pyhocon/#example-of-hocon-file).
For full HOCON capabilities see
[here](https://github.com/chimpler/pyhocon/#example-of-hocon-file).

## Parse env vars

Expand Down Expand Up @@ -202,11 +206,13 @@ is equivalent to
}
```

Note that when using `.env` source, the strict mode is disabled and value might be casted.
Note that when using `.env` source, the strict mode is disabled and value might
be casted.

## Parse CLI arguments

Same as env vars parse (dashes are converted to underscore, e.g. `TEST_A``--test-a`).
Same as env vars parse (dashes are converted to underscore, e.g. `TEST_A`
`--test-a`).

## CLI usage

Expand Down
5 changes: 3 additions & 2 deletions confs/simple.json
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
{
"hello": "bonjour"
}
"hello": "bonjour",
"foo": ["bar"]
}
3 changes: 3 additions & 0 deletions confs/simple.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
hello: bonjour
foo:
- bar
4 changes: 4 additions & 0 deletions dataconf/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,14 @@
from dataconf.main import dumps
from dataconf.main import env
from dataconf.main import file
from dataconf.main import HOCON
from dataconf.main import load
from dataconf.main import loads
from dataconf.main import multi
from dataconf.main import parse
from dataconf.main import string
from dataconf.main import url
from dataconf.main import YAML
from dataconf.version import __version__

__all__ = [
Expand All @@ -25,5 +27,7 @@
"cli",
"multi",
"parse",
"YAML",
"HOCON",
"__version__",
]
29 changes: 26 additions & 3 deletions dataconf/main.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,20 @@
import contextlib
import os
from typing import List
from typing import Type
from urllib.parse import urlparse
from urllib.request import urlopen

from dataconf import utils
from dataconf.exceptions import MalformedConfigException
from pyhocon import ConfigFactory
from pyhocon import HOCONConverter
from pyhocon.config_parser import ConfigTree
import pyparsing
from yaml import safe_load

HOCON = 1
YAML = 2


def parse(
Expand Down Expand Up @@ -44,15 +51,31 @@ def dict(self, obj: str, **kwargs) -> "Multi":
conf = ConfigFactory.from_dict(obj)
return Multi(self.confs + [conf], self.strict, **kwargs)

def string(self, s: str, **kwargs) -> "Multi":
def string(self, s: str, loader: str = HOCON, **kwargs) -> "Multi":

if loader == YAML:
data = safe_load(s)
return self.dict(data, **kwargs)

conf = ConfigFactory.parse_string(s)
return Multi(self.confs + [conf], self.strict, **kwargs)

def url(self, uri: str, **kwargs) -> "Multi":
conf = ConfigFactory.parse_URL(uri)
def url(self, uri: str, timeout: int = 10, **kwargs) -> "Multi":
path = urlparse(uri).path
if path.endswith(".yaml") or path.endswith(".yml"):
with contextlib.closing(urlopen(uri, timeout=timeout)) as fd:
s = fd.read().decode("utf-8")
return self.string(s, loader=YAML, **kwargs)

conf = ConfigFactory.parse_URL(uri, timeout=timeout)
return Multi(self.confs + [conf], self.strict, **kwargs)

def file(self, path: str, **kwargs) -> "Multi":
if path.endswith(".yaml") or path.endswith(".yml"):
with open(path, "r") as f:
data = safe_load(f)
return self.dict(data, **kwargs)

conf = ConfigFactory.parse_file(path)
return Multi(self.confs + [conf], self.strict, **kwargs)

Expand Down
1 change: 0 additions & 1 deletion dataconf/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,6 @@ def is_optional(type: Type):


def __parse(value: any, clazz: Type, path: str, strict: bool, ignore_unexpected: bool):

if is_dataclass(clazz):

if not isinstance(value, ConfigTree):
Expand Down
Loading

0 comments on commit b3a0cb8

Please sign in to comment.