Skip to content

Commit

Permalink
Introduce black formatting
Browse files Browse the repository at this point in the history
[Black](https://github.com/psf/black) is emerging as a more and more
popular code format for Python, also as a formatting tool it makes
development easier. As of this commit Pconf will follow black as well
while maintaining flake8 checks as well.
  • Loading branch information
andrasmaroy committed Nov 19, 2019
1 parent 3263699 commit 502d00c
Show file tree
Hide file tree
Showing 17 changed files with 423 additions and 276 deletions.
32 changes: 14 additions & 18 deletions .travis.yml
Original file line number Diff line number Diff line change
@@ -1,29 +1,25 @@
language: python
matrix:
include:
- python: 2.7
dist: trusty
sudo: false
- python: 3.4
dist: trusty
sudo: false
- python: 3.5
dist: trusty
sudo: false
- python: 3.6
dist: trusty
sudo: false
- python: 3.7
dist: xenial
sudo: true
jobs:
include:
- python: 2.7
env: BLACK=false
- python: 3.4
env: BLACK=false
- python: 3.5
env: BLACK=false
- python: 3.6
env: BLACK=true
- python: 3.7
env: BLACK=true
# command to install dependencies
install:
- python setup.py -q install
- pip install -U -r requirements-dev.txt
# command to run tests
script:
- py.test --cov=./pconf/ ./tests/
- flake8 --max-line-length=140 pconf tests
- flake8 --max-line-length=88 pconf tests
- if [ $BLACK = true ]; then black --check pconf tests; fi
# submit coverage
after_success: codecov
deploy:
Expand Down
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
[![Py Versions](https://img.shields.io/pypi/pyversions/pconf.svg?style=flat)](https://pypi.python.org/pypi/pconf)
[![Downloads](https://pepy.tech/badge/pconf)](https://pepy.tech/project/pconf)
[![Known Vulnerabilities](https://img.shields.io/snyk/vulnerabilities/github/andrasmaroy/pconf/requirements.txt.svg)](https://snyk.io/test/github/andrasmaroy/pconf?targetFile=requirements.txt)
[![Code style: black](https://img.shields.io/badge/code%20style-black-000000.svg?style=flat)](https://github.com/psf/black)

Hierarchical python configuration with files, environment variables, command-line arguments.

Expand Down
30 changes: 23 additions & 7 deletions pconf/pconf.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,10 @@ class Pconf(object):
After sources are set the values can be accessed by calling get()
"""

__hierarchy = []

merger = Merger(
[(dict, ["merge"])],
["override"],
["override"]
)
merger = Merger([(dict, ["merge"])], ["override"], ["override"])

@classmethod
def get(cls):
Expand Down Expand Up @@ -83,7 +80,16 @@ def argv(cls, name, short_name=None, type=None, help=None):
cls.__hierarchy.append(argv.Argv(name, short_name, type, help))

@classmethod
def env(cls, separator=None, match=None, whitelist=None, parse_values=None, to_lower=None, convert_underscores=None, docker_secrets=None):
def env(
cls,
separator=None,
match=None,
whitelist=None,
parse_values=None,
to_lower=None,
convert_underscores=None,
docker_secrets=None,
):
"""Set environment variables as a source.
By default all environment variables available to the process are used.
Expand All @@ -104,7 +110,17 @@ def env(cls, separator=None, match=None, whitelist=None, parse_values=None, to_l
their postfix removed and the content of the file pointed by
their original value.
"""
cls.__hierarchy.append(env.Env(separator, match, whitelist, parse_values, to_lower, convert_underscores, docker_secrets))
cls.__hierarchy.append(
env.Env(
separator,
match,
whitelist,
parse_values,
to_lower,
convert_underscores,
docker_secrets,
)
)

@classmethod
def file(cls, path, encoding=None, parser=None):
Expand Down
22 changes: 12 additions & 10 deletions pconf/store/argv.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ class Argv(object):
parser = None

def __init__(self, name, short_name=None, type=None, help=None):
if not name.startswith('-') or name.count(' ') != 0:
if not name.startswith("-") or name.count(" ") != 0:
raise ValueError

if Argv.parser is None:
Expand All @@ -15,22 +15,24 @@ def __init__(self, name, short_name=None, type=None, help=None):
self.results = {}

args = {}
args['help'] = help
args["help"] = help
if type == bool:
args['action'] = 'store_true'
args["action"] = "store_true"
# types supported by literal_eval
elif type in [dict, list, tuple]:
args['type'] = literal_eval
elif type == 'repeated_list':
args['action'] = 'append'
args["type"] = literal_eval
elif type == "repeated_list":
args["action"] = "append"
else:
args['type'] = type
args["type"] = type

if name.lstrip('-').count('-') != 0:
args['dest'] = name.lstrip('-')
if name.lstrip("-").count("-") != 0:
args["dest"] = name.lstrip("-")

if short_name is not None:
Argv.parser.add_argument(name, short_name, default=argparse.SUPPRESS, **args)
Argv.parser.add_argument(
name, short_name, default=argparse.SUPPRESS, **args
)
else:
Argv.parser.add_argument(name, default=argparse.SUPPRESS, **args)

Expand Down
25 changes: 17 additions & 8 deletions pconf/store/env.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,16 @@


class Env(object):
def __init__(self, separator=None, match=None, whitelist=None, parse_values=False, to_lower=False, convert_underscores=False, docker_secrets=None):
def __init__(
self,
separator=None,
match=None,
whitelist=None,
parse_values=False,
to_lower=False,
convert_underscores=False,
docker_secrets=None,
):
self.separator = separator
self.match = match
self.whitelist = whitelist
Expand Down Expand Up @@ -77,23 +86,23 @@ def __merge_split(self, split, env_vars):
def __try_parse(self, env_vars):
for key, value in iteritems(env_vars):
try:
if value.lower() == 'true':
if value.lower() == "true":
env_vars[key] = True
elif value.lower() == 'false':
elif value.lower() == "false":
env_vars[key] = False
else:
env_vars[key] = literal_eval(value)
except (ValueError, SyntaxError):
pass

def __handle_docker_secret(self, key, value):
postfix = '_FILE'
postfix = "_FILE"
if key.endswith(postfix):
try:
with open(value, 'r') as f:
self.vars[key[0:-len(postfix)]] = f.read().strip()
with open(value, "r") as f:
self.vars[key[0 : -len(postfix)]] = f.read().strip()
except IOError:
warn('IOError when opening {}'.format(value), UserWarning)
warn("IOError when opening {}".format(value), UserWarning)

def __gather_vars(self):
self.vars = {}
Expand All @@ -116,7 +125,7 @@ def __to_lower(self, key):
return key.lower()

def __convert_underscores(self, key):
return key.replace('_', '-')
return key.replace("_", "-")

def __change_keys(self, env_vars, operation):
new_dict = {}
Expand Down
23 changes: 12 additions & 11 deletions pconf/store/file.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@
import yaml

from sys import version_info
if (version_info.major < 3):

if version_info.major < 3:
import ConfigParser
from StringIO import StringIO
else:
Expand All @@ -14,27 +15,27 @@

def parse_ini(content):
config = ConfigParser.ConfigParser(allow_no_value=True)
if (version_info.major < 3):
if version_info.major < 3:
config.readfp(StringIO(content))
else:
config.read_string(content)
if len(config.sections()) == 0:
return dict(config.items('DEFAULT'))
return dict(config.items("DEFAULT"))
result = {}
for section in config.sections():
result[section] = dict(config.items(section))
return result


class File():
class File:
ENCODINGS = {
'ini': parse_ini,
'json': json.loads,
'raw': literal_eval,
'yaml': yaml.safe_load
"ini": parse_ini,
"json": json.loads,
"raw": literal_eval,
"yaml": yaml.safe_load,
}

def __init__(self, path, encoding='raw', parser=None):
def __init__(self, path, encoding="raw", parser=None):
self.__read_file(path)
self.__set_encoding(encoding, parser)
self.__parse_content()
Expand All @@ -45,10 +46,10 @@ def get(self):

def __read_file(self, path):
try:
with open(path, 'r') as f:
with open(path, "r") as f:
self.content = f.read()
except IOError:
warn('IOError when opening {}'.format(path), UserWarning)
warn("IOError when opening {}".format(path), UserWarning)
self.content = {}

def __set_encoding(self, encoding, parser=None):
Expand Down
11 changes: 6 additions & 5 deletions requirements-dev.txt
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
codecov==2.0.15
flake8==3.7.9
mock==3.0.5
pytest # pyup: ignore
pytest-cov # pyup: ignore
black; python_version > '3.5'
codecov
flake8
mock
pytest
pytest-cov
24 changes: 12 additions & 12 deletions tests/integration/integration_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,15 +8,15 @@ class IntegrationBase(TestCase):
def setUp(self):
Pconf.clear()
IntegrationBase.result = {
'tuple': (123, 'string'),
'int': 123,
'float': 1.23,
'list': ['list1', 'list2', {'dict-in-list': 'value'}],
'complex': (1+2j),
'bool': True,
'key': 'value',
'boolstring': 'false',
'string-with-specials': 'Test!@#$%^&*()-_=+[]{};:,<.>/?\\\'"`~',
'dict': {'dict': 'value', 'list-in-dict': ['nested-list1', 'nested-list2']},
'secret': 'secret'
}
"tuple": (123, "string"),
"int": 123,
"float": 1.23,
"list": ["list1", "list2", {"dict-in-list": "value"}],
"complex": (1 + 2j),
"bool": True,
"key": "value",
"boolstring": "false",
"string-with-specials": "Test!@#$%^&*()-_=+[]{};:,<.>/?\\'\"`~",
"dict": {"dict": "value", "list-in-dict": ["nested-list1", "nested-list2"]},
"secret": "secret",
}
12 changes: 7 additions & 5 deletions tests/integration/test_integration_argv.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,26 +5,28 @@

class TestIntegrationArgv(IntegrationBase):
def test_integration(self):
IntegrationBase.result.pop('secret')
IntegrationBase.result.pop("secret")

sys.argv.append("pconf")
sys.argv.append("--bool")
sys.argv.append("--boolstring")
sys.argv.append("false")
sys.argv.append("--dict")
sys.argv.append("{ \"dict\": \"value\", \"list-in-dict\": [ \"nested-list1\", \"nested-list2\" ] }")
sys.argv.append(
'{ "dict": "value", "list-in-dict": [ "nested-list1", "nested-list2" ] }'
)
sys.argv.append("--float")
sys.argv.append("1.23")
sys.argv.append("--int")
sys.argv.append("123")
sys.argv.append("--key")
sys.argv.append("value")
sys.argv.append("--list")
sys.argv.append("[ \"list1\", \"list2\", { \"dict-in-list\": \"value\" } ]")
sys.argv.append('[ "list1", "list2", { "dict-in-list": "value" } ]')
sys.argv.append("--string-with-specials")
sys.argv.append("Test!@#$%^&*()-_=+[]{};:,<.>/?\\\'\"`~")
sys.argv.append("Test!@#$%^&*()-_=+[]{};:,<.>/?\\'\"`~")
sys.argv.append("--tuple")
sys.argv.append("(123, \"string\")")
sys.argv.append('(123, "string")')
sys.argv.append("--complex")
sys.argv.append("1+2j")

Expand Down
48 changes: 25 additions & 23 deletions tests/integration/test_integration_env.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,35 +6,37 @@
class TestIntegrationArgv(IntegrationBase):
def test_integration(self):
os.environ["BOOL"] = "true"
os.environ["BOOLSTRING"] = "\"false\""
os.environ["DICT"] = "{ \"dict\": \"value\", \"list-in-dict\": [ \"nested-list1\", \"nested-list2\" ] }"
os.environ["BOOLSTRING"] = '"false"'
os.environ[
"DICT"
] = '{ "dict": "value", "list-in-dict": [ "nested-list1", "nested-list2" ] }'
os.environ["FLOAT"] = "1.23"
os.environ["INT"] = "123"
os.environ["KEY"] = "value"
os.environ["LIST"] = "[ \"list1\", \"list2\", { \"dict-in-list\": \"value\" } ]"
os.environ["STRING_WITH_SPECIALS"] = "Test!@#$%^&*()-_=+[]{};:,<.>/?\\\'\"`~"
os.environ["TUPLE"] = "(123, \"string\")"
os.environ["LIST"] = '[ "list1", "list2", { "dict-in-list": "value" } ]'
os.environ["STRING_WITH_SPECIALS"] = "Test!@#$%^&*()-_=+[]{};:,<.>/?\\'\"`~"
os.environ["TUPLE"] = '(123, "string")'
os.environ["COMPLEX"] = "1+2j"
os.environ["SECRET_FILE"] = "./tests/integration/example_secret"

Pconf.env(
whitelist=[
'KEY',
'INT',
'FLOAT',
'COMPLEX',
'LIST',
'DICT',
'TUPLE',
'BOOL',
'BOOLSTRING',
'STRING_WITH_SPECIALS',
'SECRET_FILE'
],
parse_values=True,
to_lower=True,
convert_underscores=True,
docker_secrets=['SECRET_FILE']
)
whitelist=[
"KEY",
"INT",
"FLOAT",
"COMPLEX",
"LIST",
"DICT",
"TUPLE",
"BOOL",
"BOOLSTRING",
"STRING_WITH_SPECIALS",
"SECRET_FILE",
],
parse_values=True,
to_lower=True,
convert_underscores=True,
docker_secrets=["SECRET_FILE"],
)
config = Pconf.get()
self.assertEqual(config, IntegrationBase.result)
Loading

0 comments on commit 502d00c

Please sign in to comment.