diff --git a/CHANGELOG.md b/CHANGELOG.md index e14bbd7..a687bae 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,9 @@ ### Fixed ### Changed + +- use pymdgen for automatic api docs (#22) + ### Deprecated ### Removed ### Security diff --git a/Ctl/requirements-docs.txt b/Ctl/requirements-docs.txt index 33a14f5..2c61132 100644 --- a/Ctl/requirements-docs.txt +++ b/Ctl/requirements-docs.txt @@ -1,2 +1,3 @@ -markdown-include==0.5.1 +markdown-include>=0.5,<1 mkdocs>=1.0.0,<2.0.0 +pymdgen<2 diff --git a/confu/cli.py b/confu/cli.py index f02ca5b..5279014 100644 --- a/confu/cli.py +++ b/confu/cli.py @@ -1,3 +1,8 @@ +""" +functions that allow you to generate CLI parameters from a confu schema for +argparse or click +""" + try: import click except ImportError: @@ -5,12 +10,35 @@ def option_name(path, delimiter="--"): - """returns a cli option name from attribute path""" + """ + Returns a cli option name from attribute path + + **Arguments** + + - path (`list`): attribute path + - delimiter (`str`): delimiter for nested attributes + + **Returns** + + cli option name (`str`) + """ + return "--{}".format(delimiter.join(path).replace("_", "-")) def destination_name(path, delimiter="__"): - """returns a cli option destination name from attribute path""" + """ + Returns a cli option destination name from attribute path + + **Arguments** + + - path (`list`): attribute path + - delimiter (`str`): delimiter for nested attributes + + **Returns** + + cli destination name (`str`) + """ return "{}".format(delimiter.join(path)) @@ -26,6 +54,22 @@ def default(value, path, defaults): def argparse_options(parser, schema, defaults=None, attributes=None): + + """ + Add cli options to an argparse ArgumentParser instance + + **Arguments** + + - parser (`argparse.ArgumentParser`) + - schema (`Schema`) + + **Keyword Arguments** + + - defaults (`dict`): if specified will override defaults from here + - attributes (`list`): can hold a list of attribute names. + if specified only matching attributes will be aded + """ + def optionize(attribute, path): if not attribute.cli: return @@ -55,6 +99,23 @@ def optionize(attribute, path): class click_options(object): + + """ + Add cli options to a click decorated function + + Use like a decorator + + **Arguments** + + - schema (`Schema`) + + **Keyword Arguments** + + - defaults (`dict`): if specified will override defaults from here + - attributes (`list`): can hold a list of attribute names. + if specified only matching attributes will be aded + """ + def __init__(self, schema, defaults=None, attributes=None): self.schema = schema self.defaults = defaults diff --git a/confu/config.py b/confu/config.py index a5c7319..a8d4ad7 100644 --- a/confu/config.py +++ b/confu/config.py @@ -1,3 +1,6 @@ +""" +config management +""" from __future__ import absolute_import, division, print_function import collections @@ -13,9 +16,14 @@ class for storing and manipulating config data def __init__(self, schema, data=None, meta=None): """ - `schema` confu.schema object - `data` dict to set initial data - `meta` any addition metadata to pass along with config + **Arguments** + + - schema (`confu.schema`): schema object + + **Keyword Arguments** + + - data (`dict`): dict to set initial data + - meta (`dict`): any additional metadata to pass along with config """ self._base_data = None self._data = None diff --git a/confu/exceptions.py b/confu/exceptions.py index f3114c0..d6677be 100644 --- a/confu/exceptions.py +++ b/confu/exceptions.py @@ -1,4 +1,8 @@ class SoftDependencyError(ImportError): + """ + Raised when a feature requires a dependency that is missing + """ + def __init__(self, dep_name): super(SoftDependencyError, self).__init__( "To use this feature this dependency is required: {}".format(dep_name) @@ -6,7 +10,20 @@ def __init__(self, dep_name): class ValidationErrorBase(ValueError): + """ + Config validation error interface + """ + def __init__(self, attribute, path, value, reason): + """ + **Arguments** + + - attribute (`Attribute`): confu attribute instance + - path (`list`): attribute path + - value (`mixed`): value that caused the validation error + - reason (`str`): human readable reason message for validation error + """ + msg = "{}: {}".format(path, reason) self.details = { "path": path, @@ -18,6 +35,9 @@ def __init__(self, attribute, path, value, reason): @property def pretty(self): + """ + pretty formatted error message + """ return "{}: {}".format( ".".join([str(i) for i in self.details["path"]]), self.details["reason"] ) @@ -34,13 +54,25 @@ def __eq__(self, other): class ValidationWarning(ValidationErrorBase): + """ + Config validation warning + """ + pass class ValidationError(ValidationErrorBase): + """ + Config validation error + """ + pass class ApplyDefaultError(ValidationErrorBase): + """ + Raised when an exception occured during apply_defaults + """ + def __str__(self): return self.pretty diff --git a/confu/generator.py b/confu/generator.py index a5350da..107754a 100644 --- a/confu/generator.py +++ b/confu/generator.py @@ -1,11 +1,31 @@ +""" +Schema to data generators +""" from confu.schema import Schema, Attribute class ConfigGenerator(object): + """ + Generate config from schema using default values + """ + def __init__(self): pass def generate(self, schema): + + """ + Generate confug from schema using default values + + **Arguments** + + - schema (`Schema|Attribute`): confu schema object + + **Returns** + + generated config `dict` + """ + if isinstance(schema, Schema): config = {} for name, attribute in schema.attributes(): @@ -21,6 +41,19 @@ def generate(self, schema): def generate(schema, generator=None): """ generate config shortcut function + + **Arguments** + + - schema (`Schema`): confu schema object + + **Keyword Arguments** + + - generator (`ConfigGenerator`): generator object, if non is supplied + will instantiate a ConfigGenerator instance itself + + **Returns** + + generated config (`dict`) """ if not generator: generator = ConfigGenerator() diff --git a/confu/schema/core.py b/confu/schema/core.py index da771fa..1255b25 100644 --- a/confu/schema/core.py +++ b/confu/schema/core.py @@ -1,3 +1,9 @@ +""" +Fundamental schema attributes + +These can be imported directly from `confu.schema` +""" + import os import six import collections @@ -28,20 +34,20 @@ def __init__(self, name="", **kwargs): **Keyword Arguments** - - `name` (str): describes the attribute name, if not specified + - name (`str`): describes the attribute name, if not specified explicitly will be set through the schema that instantiates the attribute. - - `default` (mixed): the default value of this attribute. Once a default + - default (`mixed`): the default value of this attribute. Once a default value is set, schema validation will no longer raise a validation error if the attribute is missing from the configuration. - - `choices` (list): if specified on values in this list may be set + - choices (`list`): if specified on values in this list may be set for this attribute - - `help` (str): help description - - `cli` (bool=True|function): enable CLI support for this attribute - - `deprecated` (str): version id of when this attribute will be deprecated - - `added` (str): version id of when this attribute was added to the schema - - `removed` (str): version id of when this attribute will be removed + - help (`str`): help description + - cli (`bool=True|function`): enable CLI support for this attribute + - deprecated (`str`): version id of when this attribute will be deprecated + - added (`str`): version id of when this attribute was added to the schema + - removed (`str`): version id of when this attribute will be removed """ # attribute name in the schema @@ -135,8 +141,8 @@ def validate(self, value, path, **kwargs): **Arguments** - - `value` (mixed): the value to validate - - `path` (list): current path in the config schema, this is mostly + - value (`mixed`): the value to validate + - path (`list`): current path in the config schema, this is mostly used to identify where an error occured when validating against config data, you can pass an empty list to it when calling this manually @@ -348,20 +354,20 @@ def __init__(self, name=None, item=None, **kwargs): **Keyword Arguments** - - `name` (str): describes the attribute name, if not specified + - name (`str`): describes the attribute name, if not specified explicitly will be set through the schema that instantiates the attribute. - - `item` (Attribute): allows you to specify an arbitrary attribute + - item (`Attribute`): allows you to specify an arbitrary attribute to use for all values in the list. - - `default` (mixed): the default value of this attribute. Once a default + - default (`mixed`): the default value of this attribute. Once a default value is set, schema validation will no longer raise a validation error if the attribute is missing from the configuration. - - `help` (str): help description - - `cli` (bool=True): enable CLI support for this attribute - - `deprecated` (str): version id of when this attribute will be deprecated - - `added` (str): version id of when this attribute was added to the schema - - `removed` (str): version id of when this attribute will be removed + - help (`str`): help description + - cli (`bool=True`): enable CLI support for this attribute + - deprecated (`str`): version id of when this attribute will be deprecated + - added (`str`): version id of when this attribute was added to the schema + - removed (`str`): version id of when this attribute will be removed """ if not isinstance(item, Attribute): @@ -480,21 +486,21 @@ def __init__(self, *args, **kwargs): **Keyword Arguments** - - `item` (Attribute): allows you to specify an arbitrary attribute + - item (`Attribute`): allows you to specify an arbitrary attribute to use for all values in the schema. This is only allowed if your schema does **NOT** explicitly set any attributes in it's definition - - `name` (str): describes the attribute name, if not specified + - name (`str`): describes the attribute name, if not specified explicitly will be set through the schema that instantiates the attribute. - - `default` (mixed): the default value of this attribute. Once a default + - default (`mixed`): the default value of this attribute. Once a default value is set, schema validation will no longer raise a validation error if the attribute is missing from the configuration. - - `help` (str): help description - - `cli` (bool=True): enable CLI support for this attribute - - `deprecated` (str): version id of when this attribute will be deprecated - - `added` (str): version id of when this attribute was added to the schema - - `removed` (str): version id of when this attribute will be removed + - help (`str`): help description + - cli (`bool=True`): enable CLI support for this attribute + - deprecated (`str`): version id of when this attribute will be deprecated + - added (`str`): version id of when this attribute was added to the schema + - removed (`str`): version id of when this attribute will be removed """ # collect attributes @@ -544,15 +550,15 @@ def validate(self, config, path=None, errors=None, warnings=None): **Attributes** - - `config` (dict): config to validate + - config (`dict`): config to validate **Keyword Attributes** - - `path` (list): current path in the config data, this can be + - path (`list`): current path in the config data, this can be ignored on the initial call and will be set automatically on any subsequent calls (nested schemas) - - `errors` (ValidationErrorProcessor) - - `warnigns` (ValidationErrorProcessor) + - errors (`ValidationErrorProcessor`) + - warnigns (`ValidationErrorProcessor`) """ if path is None: @@ -637,12 +643,12 @@ def validate(schema, config, raise_errors=False, log=None, **kwargs): **Arguments** - - `schema` (Schema): schema instance - - `config` (dict|munge.Config) + - schema (`Schema`): schema instance + - config (`dict|munge.Config`) **Keyword Arguments** - - `raise_errors` (bool=False): if `True` will raise a ValidationError exception + - raise_errors (`bool=False`): if `True` will raise a ValidationError exception if it encounters validation errors If `False` it will instead collect errors and warnings and return a tuple: @@ -651,7 +657,7 @@ def validate(schema, config, raise_errors=False, log=None, **kwargs): success(bool), errors(CollectValidationExceptions), warnings(CollectValidationException) ``` - - `log` (callable): function to use to log errors, will be passed + - log (`callable`): function to use to log errors, will be passed a str message - any additional kwargs will be passed on to `Schema.validate` """ @@ -686,9 +692,9 @@ def apply_default(config, attribute, path): **Arguments** - - `config` (dict): the config dictonary - - `attribute` (Attribute): attribute instance - - `path` (list(str)): full path of the attribute in the schema + - config (`dict`): the config dictonary + - attribute (`Attribute`): attribute instance + - path (`list(str)`): full path of the attribute in the schema """ _config = config @@ -760,8 +766,8 @@ def apply_defaults(schema, config, debug=False): **Arguments** - - `schema` (Schema): schema instance - - `config` (dict): the config dictonary + - schema (`Schema`): schema instance + - config (`dict`): the config dictonary """ if isinstance(schema, ProxySchema): diff --git a/confu/schema/inet.py b/confu/schema/inet.py index 86f75f6..98f4fa2 100644 --- a/confu/schema/inet.py +++ b/confu/schema/inet.py @@ -1,3 +1,13 @@ +""" +Attributes that deal with networking specific values such as emails, urls and ip addresses + +These can be imported directly from `confu.schema` + +## Requirements + +- `ipaddress` for ip address validation +""" + import re try: @@ -100,22 +110,22 @@ def __init__(self, name="", protocol=None, **kwargs): **Keyword Arguments** - - `name` (str): describes the attribute name, if not specified + - name (`str`): describes the attribute name, if not specified explicitly will be set through the schema that instantiates the attribute. - - `protocol` (int): ip version, can be 4, 6 or None - if it is none + - protocol (`int`): ip version, can be 4, 6 or None - if it is none the attribute can hold either a v4 or a v6 IP address. - - `default` (mixed): the default value of this attribute. Once a default + - default (`mixed`): the default value of this attribute. Once a default value is set, schema validation will no longer raise a validation error if the attribute is missing from the configuration. - - `choices` (list): if specified on values in this list may be set + - choices (`list`): if specified on values in this list may be set for this attribute - - `help` (str): help description - - `cli` (bool=True): enable CLI support for this attribute - - `deprecated` (str): version id of when this attribute will be deprecated - - `added` (str): version id of when this attribute was added to the schema - - `removed` (str): version id of when this attribute will be removed + - help (`str`): help description + - cli (`bool=True`): enable CLI support for this attribute + - deprecated (`str`): version id of when this attribute will be deprecated + - added (`str`): version id of when this attribute was added to the schema + - removed (`str`): version id of when this attribute will be removed """ super(IpAddress, self).__init__(name=name, **kwargs) diff --git a/confu/util.py b/confu/util.py index 8c9a6ab..aa884da 100644 --- a/confu/util.py +++ b/confu/util.py @@ -1,6 +1,19 @@ +""" +utility functions / classes +""" + + def config_parser_dict(config): """ Takes a configparser.ConfigParser instance and returns a dict of sections and their keys and values + + **Arguments** + + - config (`configparser.ConfigParsers`) + + **Returns** + + dict """ return {s: dict(config.items(s)) for s in config.sections()} diff --git a/docs/attributes.md b/docs/attributes.md index 4735683..3ba709e 100644 --- a/docs/attributes.md +++ b/docs/attributes.md @@ -1,113 +1,7 @@ -All schema attributes can be imported from the `confu.schema` namespace +All confu schema attributes can be imported from the `confu.schema` namespace -```py -from confu.schema import Str -``` +Please check these api references for a list of which attributes are available: -# Attribute properties +- [schema.core](/api/confu.schema.core): fundamental attributes +- [schema.inet](/api/confu.schema.inet): attributes for network related values -All attributes have these possible properties - -**name** - `str` - default: `""` -Used to identify the attribute during validation, mandatory on first level attributes, can be omitted on attributes that are describing list items. - -**default** - `mixed` - default: `None` -The default value that will be used if no value is set for the attribute. This itself defaults to -None and in that case the attribute will be seen as mandatory and raise a validation error -if not set. - -**help** - `str` - default: `None` -Help text - should describe what the attribute is for - -**added** - `str|int|tuple` - default: `None` -Version in which this attribute was added - -**deprecated** - `str|int|tuple` - default: `None` -Version in which this attribute will be deprecated - -**removed** - `str|int|tuple` - default: `None` -Version in which this attributed will be removed - -**cli** - `bool` - default: `True` -Enable this attribute when working with a CLI interface - -# Attribute Types - -Confu comes with several commonly used attributes out of the box. - -They can all be imported from `confu.schema` - -### Schema -holds a collection of attributes - can nest additional schemas - -**Special properties** - -*item* - `Attribute` - default: `None`: attribute that describes items held by this schema -if the schema describes a dict with arbitrary keys. - - -!!! Tip "item property cannot be set on schemas with defined attributes" - The `item` property is solely used to allow a schema to validate a dictonary - that has arbitrary key names. As such trying to set the `item` property on a schema - that has attributes defined within will raise a `ValueError` - - -### Str -validates a string value - -**Special properties** - -*blank* - `bool` - default: `False`: allow blank values - -### Int -validates an integer value - -### Float -validates a float value - -### List -validates a list - -**Special Properties** - -*item* - `Attribute` - default: `None`: attribute that describes items held by this list - -```py -my_list = confu.schema.List(name="my_list", item=confu.schema.Str()) -``` - -### File -validates a file path, will raise a ValidationError if the specified file does not exist - -### Directory -validates a directory path, the default behaviour is to raide a ValidationError if the specified -directory does not exist - -**Special Properties** - - - *create* - `bool` - default: `False`: if True will create the directory - -### Email -validates an email address - -### Url -validates a url - -Special Properties: - - *schemes* - `list` - default: `[]`: A list of supported schemes - an empty list will allow all schemes - -### IpAddress -validates a v4 or v6 ip address - - -!!! Tip "Requirements" - To use this attribute please install the python ipaddress module - - ``` - pip install ipaddress - ``` - - -**Special Properties** - - - *protocol* - `int` - default: `None`: Specifies the protocol can be `4`, `6` or `None`. If `None` both 4 and 6 type addresses are allowed. diff --git a/examples/configparser/example.py b/examples/configparser/example.py index 7e84a6c..4f2a850 100644 --- a/examples/configparser/example.py +++ b/examples/configparser/example.py @@ -7,8 +7,8 @@ # this schema describes the [server] section class ServerSchema(Schema): - host = IpAddress("host") - port = Int("port", default=80) + host = IpAddress() + port = Int(default=80) # this schema describes the entire config diff --git a/mkdocs.yml b/mkdocs.yml index 3f205c5..059c5e4 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -3,12 +3,22 @@ site_url: https://github.com/20c/confu theme: readthedocs -pages: +nav: - Home: index.md - Quickstart: quickstart.md - Attributes: attributes.md - Examples: examples.md - CLI: cli.md + - API: + - confu.config: api/confu.config.md + - confu.cli: api/confu.cli.md + - confu.exceptions: api/confu.exceptions.md + - confu.generator: api/confu.generator.md + - confu.schema.core: api/confu.schema.core.md + - confu.schema.inet: api/confu.schema.inet.md + - confu.util: api/confu.util.md markdown_extensions: - markdown_include.include + - admonition + - pymdgen