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} {"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]] [[myapp]]
important_number = 8237 important_number = 8237
@ -43,14 +44,13 @@ from copy import deepcopy
from .freezedry import freezedryable, rehydrate from .freezedry import freezedryable, rehydrate
class InvalidConfigError(Exception): class InvalidConfigError(Exception):
pass pass
# The Table and Array terms from the TOML convention essentially # The Table and Array terms from the TOML convention essentially
# map directly to Dictionaries (Tables), and Lists (Arrays) # map directly to Dictionaries (Tables), and Lists (Arrays)
class _ConfigDefinition(ABC): class _ConfigDefinition(ABC):
def __init__(self, default=None, optional=False, helptext=""): def __init__(self, default=None, optional=False, helptext=""):
self.default = default self.default = default
@ -75,6 +75,7 @@ class BoolDef(_ConfigDefinition):
if not isinstance(value, bool): if not isinstance(value, bool):
raise InvalidConfigError("Config value must be a boolean") raise InvalidConfigError("Config value must be a boolean")
@freezedryable @freezedryable
class IntDef(_ConfigDefinition): class IntDef(_ConfigDefinition):
def __init__(self, default=None, minval=None, maxval=None, def __init__(self, default=None, minval=None, maxval=None,
@ -93,6 +94,7 @@ class IntDef(_ConfigDefinition):
raise InvalidConfigError("Config value must be <= " + raise InvalidConfigError("Config value must be <= " +
str(self.maxval)) str(self.maxval))
@freezedryable @freezedryable
class StringDef(_ConfigDefinition): class StringDef(_ConfigDefinition):
def __init__(self, default="", minlength=None, maxlength=None, def __init__(self, default="", minlength=None, maxlength=None,
@ -111,6 +113,7 @@ class StringDef(_ConfigDefinition):
raise InvalidConfigError("Config string length must be <= " + raise InvalidConfigError("Config string length must be <= " +
str(self.maxlength)) str(self.maxlength))
@freezedryable @freezedryable
class DictDef(_ConfigDefinition): class DictDef(_ConfigDefinition):
def __init__(self, default=None, optional=False, helptext=""): def __init__(self, default=None, optional=False, helptext=""):
@ -173,10 +176,10 @@ class DictDef(_ConfigDefinition):
if confdef.optional and (not include_optional): if confdef.optional and (not include_optional):
continue continue
if hasattr(confdef,"get_template"): if hasattr(confdef, "get_template"):
template[key]=confdef.get_template(include_optional) template[key] = confdef.get_template(include_optional)
else: else:
template[key]=confdef.default template[key] = confdef.default
return template return template
@ -192,27 +195,32 @@ class _ListDefMixin():
raise raise
def get_template(self, include_optional=False): def get_template(self, include_optional=False):
if hasattr(super(),"get_template"): if hasattr(super(), "get_template"):
return [super().get_template(include_optional)] return [super().get_template(include_optional)]
else: else:
return [self.default] return [self.default]
@freezedryable @freezedryable
class BoolListDef(_ListDefMixin, BoolDef): class BoolListDef(_ListDefMixin, BoolDef):
pass pass
@freezedryable @freezedryable
class IntListDef(_ListDefMixin, IntDef): class IntListDef(_ListDefMixin, IntDef):
pass pass
@freezedryable @freezedryable
class StringListDef(_ListDefMixin, StringDef): class StringListDef(_ListDefMixin, StringDef):
pass pass
@freezedryable @freezedryable
class DictListDef(_ListDefMixin, DictDef): class DictListDef(_ListDefMixin, DictDef):
pass pass
@freezedryable @freezedryable
class ConfDefinition(DictDef): class ConfDefinition(DictDef):
pass pass
@ -238,7 +246,6 @@ class ConfigManager():
else: # load from file else: # load from file
return toml.load(source) return toml.load(source)
def load(self, source): def load(self, source):
""" """
Load a config source into the ConfigManager, replacing any existing config. Load a config source into the ConfigManager, replacing any existing config.
@ -258,13 +265,12 @@ class ConfigManager():
value and completely replaced. value and completely replaced.
Args: Args:
source: Either the root dict of a data structure to load directly, a filepath to a TOML file, source: Either the root dict of a data structure to load directly, a filepath to a TOML
or an open TOML file. file, or an open TOML file.
""" """
self._overlay(self._load_source(source), self.root_config) self._overlay(self._load_source(source), self.root_config)
self._overlay(self.frozen_config, self.root_config) self._overlay(self.frozen_config, self.root_config)
def freeze_value(self, bundle_name, *field_names): def freeze_value(self, bundle_name, *field_names):
""" """
Freeze the given config field so that subsequent calls to ``load`` and ``load_overlay`` 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. 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 names = (bundle_name,) + field_names
target_field = self.root_config target_field = self.root_config
@ -289,12 +295,8 @@ class ConfigManager():
frozen_value[name] = {} frozen_value[name] = {}
frozen_value = frozen_value[name] frozen_value = frozen_value[name]
frozen_value[names[-1]] = target_field[names[-1]] frozen_value[names[-1]] = target_field[names[-1]]
def add_confdef(self, bundle_name, confdef): def add_confdef(self, bundle_name, confdef):
""" """
Stores a ConfigDefinition for future use when validating the corresponding config bundle 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. bundle_name (str) : The name to store the config definition under.
confdef (ConfigDefinition): The populated ConfigDefinition to store. confdef (ConfigDefinition): The populated ConfigDefinition to store.
""" """
self.confdefs[bundle_name]=confdef self.confdefs[bundle_name] = confdef
def add_confdefs(self, confdefs): 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: Args:
confdefs : A dict of populated ConfigDefinitions to store, using their keys as names. 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()) return list(self.root_config.keys() - self.confdefs.keys())
def _overlay(self, src, dest): def _overlay(self, src, dest):
for key in src: for key in src:
# If the key is also in the dest and both are dicts, merge them. # 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`` corresponding confdef stored in the ConfigManager. See ``get_config_bundle``
Args: Args:
bundle_names: A list of config bundle names to get. If dictionary is supplied, uses the values bundle_names: A list of config bundle names to get. If dictionary is supplied, uses
as ConfigDefinitions rather than looking up a stored one in the ConfigManager. the values as ConfigDefinitions rather than looking up a stored one in the
ConfigManager.
Returns: Returns:
A dict of config dicts, with keys matching those passed in ``bundle_names``. 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): def gen_comment(string):
return '\n# shepherd_message: ' + '\n# '.join(string.replace('#', '').splitlines()) + '\n' 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