Add general package boilerplate, setup dev install. Closes #2

master
Tom Wilson 6 years ago
parent ed8701b1e7
commit ccd5a50892

116
.gitignore vendored

@ -0,0 +1,116 @@
# ---> Python
# Byte-compiled / optimized / DLL files
__pycache__/
*.py[cod]
*$py.class
# C extensions
*.so
# Distribution / packaging
.Python
build/
develop-eggs/
dist/
downloads/
eggs/
.eggs/
lib/
lib64/
parts/
sdist/
var/
wheels/
*.egg-info/
.installed.cfg
*.egg
MANIFEST
# PyInstaller
# Usually these files are written by a python script from a template
# before PyInstaller builds the exe, so as to inject date/other infos into it.
*.manifest
*.spec
# Installer logs
pip-log.txt
pip-delete-this-directory.txt
# Unit test / coverage reports
htmlcov/
.tox/
.nox/
.coverage
.coverage.*
.cache
nosetests.xml
coverage.xml
*.cover
.hypothesis/
.pytest_cache/
# Translations
*.mo
*.pot
# Django stuff:
*.log
local_settings.py
db.sqlite3
# Flask stuff:
instance/
.webassets-cache
# Scrapy stuff:
.scrapy
# Sphinx documentation
docs/_build/
# PyBuilder
target/
# Jupyter Notebook
.ipynb_checkpoints
# IPython
profile_default/
ipython_config.py
# pyenv
.python-version
# celery beat schedule file
celerybeat-schedule
# SageMath parsed files
*.sage.py
# Environments
.env
.venv
env/
venv/
ENV/
env.bak/
venv.bak/
# Spyder project settings
.spyderproject
.spyproject
# Rope project settings
.ropeproject
# mkdocs documentation
/site
# mypy
.mypy_cache/
.dmypy.json
dmypy.json
# Pyre type checker
.pyre/

@ -18,7 +18,8 @@ This would resolve to a config bundle named "myapp" that results in the dict::
{"config_thingy_a": "foooooo!", "important_number": 8237}
Root items that are not dicts are not supported, for instance both the following TOML files would fail::
Root items that are not dicts are not supported, for instance both the following
TOML files would fail::
[[myapp]]
important_number = 8237
@ -43,14 +44,13 @@ from copy import deepcopy
from .freezedry import freezedryable, rehydrate
class InvalidConfigError(Exception):
pass
# The Table and Array terms from the TOML convention essentially
# map directly to Dictionaries (Tables), and Lists (Arrays)
class _ConfigDefinition(ABC):
def __init__(self, default=None, optional=False, helptext=""):
self.default = default
@ -75,6 +75,7 @@ class BoolDef(_ConfigDefinition):
if not isinstance(value, bool):
raise InvalidConfigError("Config value must be a boolean")
@freezedryable
class IntDef(_ConfigDefinition):
def __init__(self, default=None, minval=None, maxval=None,
@ -93,6 +94,7 @@ class IntDef(_ConfigDefinition):
raise InvalidConfigError("Config value must be <= " +
str(self.maxval))
@freezedryable
class StringDef(_ConfigDefinition):
def __init__(self, default="", minlength=None, maxlength=None,
@ -111,6 +113,7 @@ class StringDef(_ConfigDefinition):
raise InvalidConfigError("Config string length must be <= " +
str(self.maxlength))
@freezedryable
class DictDef(_ConfigDefinition):
def __init__(self, default=None, optional=False, helptext=""):
@ -173,10 +176,10 @@ class DictDef(_ConfigDefinition):
if confdef.optional and (not include_optional):
continue
if hasattr(confdef,"get_template"):
template[key]=confdef.get_template(include_optional)
if hasattr(confdef, "get_template"):
template[key] = confdef.get_template(include_optional)
else:
template[key]=confdef.default
template[key] = confdef.default
return template
@ -192,27 +195,32 @@ class _ListDefMixin():
raise
def get_template(self, include_optional=False):
if hasattr(super(),"get_template"):
if hasattr(super(), "get_template"):
return [super().get_template(include_optional)]
else:
return [self.default]
@freezedryable
class BoolListDef(_ListDefMixin, BoolDef):
pass
@freezedryable
class IntListDef(_ListDefMixin, IntDef):
pass
@freezedryable
class StringListDef(_ListDefMixin, StringDef):
pass
@freezedryable
class DictListDef(_ListDefMixin, DictDef):
pass
@freezedryable
class ConfDefinition(DictDef):
pass
@ -231,14 +239,13 @@ class ConfigManager():
and return a plain parsed dict.
"""
if isinstance(source, dict): # load from dict
return source
return source
elif isinstance(source, str): # load from pathname
with open(source, 'r') as conf_file:
return toml.load(conf_file)
else: # load from file
return toml.load(source)
def load(self, source):
"""
Load a config source into the ConfigManager, replacing any existing config.
@ -258,13 +265,12 @@ class ConfigManager():
value and completely replaced.
Args:
source: Either the root dict of a data structure to load directly, a filepath to a TOML file,
or an open TOML file.
source: Either the root dict of a data structure to load directly, a filepath to a TOML
file, or an open TOML file.
"""
self._overlay(self._load_source(source), self.root_config)
self._overlay(self.frozen_config, self.root_config)
def freeze_value(self, bundle_name, *field_names):
"""
Freeze the given config field so that subsequent calls to ``load`` and ``load_overlay``
@ -276,7 +282,7 @@ class ConfigManager():
key or series of nested keys.
"""
#Bundle names are really no different from any other nested dict
# Bundle names are really no different from any other nested dict
names = (bundle_name,) + field_names
target_field = self.root_config
@ -289,12 +295,8 @@ class ConfigManager():
frozen_value[name] = {}
frozen_value = frozen_value[name]
frozen_value[names[-1]] = target_field[names[-1]]
def add_confdef(self, bundle_name, confdef):
"""
Stores a ConfigDefinition for future use when validating the corresponding config bundle
@ -303,11 +305,12 @@ class ConfigManager():
bundle_name (str) : The name to store the config definition under.
confdef (ConfigDefinition): The populated ConfigDefinition to store.
"""
self.confdefs[bundle_name]=confdef
self.confdefs[bundle_name] = confdef
def add_confdefs(self, confdefs):
"""
Stores multiple ConfigDefinitions at once for future use when validating the corresponding config bundles
Stores multiple ConfigDefinitions at once for future use when validating the corresponding
config bundles
Args:
confdefs : A dict of populated ConfigDefinitions to store, using their keys as names.
@ -321,7 +324,6 @@ class ConfigManager():
"""
return list(self.root_config.keys() - self.confdefs.keys())
def _overlay(self, src, dest):
for key in src:
# If the key is also in the dest and both are dicts, merge them.
@ -367,8 +369,9 @@ class ConfigManager():
corresponding confdef stored in the ConfigManager. See ``get_config_bundle``
Args:
bundle_names: A list of config bundle names to get. If dictionary is supplied, uses the values
as ConfigDefinitions rather than looking up a stored one in the ConfigManager.
bundle_names: A list of config bundle names to get. If dictionary is supplied, uses
the values as ConfigDefinitions rather than looking up a stored one in the
ConfigManager.
Returns:
A dict of config dicts, with keys matching those passed in ``bundle_names``.
@ -417,4 +420,3 @@ def update_toml_message(filepath, message):
def gen_comment(string):
return '\n# shepherd_message: ' + '\n# '.join(string.replace('#', '').splitlines()) + '\n'

@ -0,0 +1,2 @@
[pytest]
pep8maxlinelength = 99

@ -0,0 +1,26 @@
from setuptools import setup
setup(name='config-spec',
version='0.3dev',
description='',
url='https://git.distreon.net/novirium/config-spec',
author='novirium',
author_email='t.wilson.au@gmail.com',
license='MIT',
packages=[],
py_modules=['configspec'],
install_requires=[
"preserve@git+https://git.distreon.net/novirium/python-preserve.git"
],
extras_require={
'dev': [
'pylint',
'autopep8',
'pytest',
'pytest-pep8',
'pytest-cov'
]
},
long_description=open('README.md').read(),
long_description_content_type='text/markdown'
)
Loading…
Cancel
Save