Skip to content

Commit

Permalink
:add: UTs of ResolverRegistry
Browse files Browse the repository at this point in the history
  • Loading branch information
b3j0f committed Feb 11, 2016
1 parent d031afc commit 838abbf
Show file tree
Hide file tree
Showing 12 changed files with 381 additions and 144 deletions.
2 changes: 1 addition & 1 deletion b3j0f/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,6 @@
# SOFTWARE.
# --------------------------------------------------------------------

# extend the b3j0f package with the utils project
# extend the b3j0f package with the conf project
from pkgutil import extend_path
__path__ = extend_path(__path__, __name__)
72 changes: 48 additions & 24 deletions b3j0f/conf/configurable/core.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@

__all__ = ['MetaConfigurable', 'Configurable']

from six import string_types
from six import string_types, add_metaclass

from b3j0f.annotation import PrivateInterceptor

Expand Down Expand Up @@ -74,6 +74,7 @@ def __call__(cls, *args, **kwargs):
__CONFIGURABLES__ = '__configurables__'


@add_metaclass(MetaConfigurable)
class Configurable(PrivateInterceptor):
"""Manage class conf synchronisation with conf resources.
Expand All @@ -82,9 +83,6 @@ class Configurable(PrivateInterceptor):
In such situation, it is possible to go back to a stable state in calling
the method `restart`. Without failure, the dirty status is canceled."""

#: ensure to apply configuration after instanciation of the configurable.
__metaclass__ = MetaConfigurable

CATEGORY = 'CONFIGURABLE' #: configuration category name.

CONFPATHS = 'paths' #: paths attribute name.
Expand All @@ -105,6 +103,8 @@ class Configurable(PrivateInterceptor):
DEFAULT_AUTOCONF = True #: default value for auto configuration.
DEFAULT_SAFE = True #: default value for safe attribute.

SUB_CONF_PREFIX = ':' #: sub conf prefix.

def __init__(
self,
conf=None, inheritedconf=DEFAULT_INHERITEDCONF, confpath=None,
Expand Down Expand Up @@ -471,27 +471,23 @@ def configure(self, conf=None, toconfigure=None, logger=None):
)

else:
unifiedconf = conf.unify()

self._configure(
unifiedconf=unifiedconf, logger=logger,
toconfigure=toconfigure
)
self._configure(conf=conf, logger=logger, toconfigure=toconfigure)

def _configure(self, unifiedconf=None, logger=None, toconfigure=None):
def _configure(self, conf=None, logger=None, toconfigure=None):
"""Configure this class with input conf only if auto_conf or
configure is true.
This method should be overriden for specific conf
:param Configuration unifiedconf: unified configuration. Default is
self.conf.unify().
:param Configuration conf: configuration model to configure. Default is
this conf.
:param bool configure: if True, force full self conf
:param toconfigure: object to configure. self if equals None.
"""

if unifiedconf is None:
unifiedconf = self.conf.unify()
if conf is None:
conf = self.conf

if toconfigure is None: # init toconfigure
toconfigure = self.toconfigure
Expand All @@ -501,22 +497,50 @@ def _configure(self, unifiedconf=None, logger=None, toconfigure=None):
for toconfigure in toconfigure:

self._configure(
unifiedconf=unifiedconf, logger=logger,
toconfigure=toconfigure
conf=conf, logger=logger, toconfigure=toconfigure
)

else:
values = [p for p in unifiedconf[Configuration.VALUES]]

if self.foreigns:
foreigns = [p for p in unifiedconf[Configuration.FOREIGNS]]
values += foreigns
sub_confs = []
params = []

for category in conf:
if category.name.startswith(self.SUB_CONF_PREFIX):
sub_confs.append(category.name)

else:
cparams = category.params
params += cparams

for param in cparams:

for parameter in values:
name = parameter.name
value = param.value

pvalue = parameter.value
setattr(toconfigure, name, pvalue)
if param.error:
continue

if self.foreigns or param.local:
setattr(toconfigure, param.name, value)

for param in params:

sub_conf_name = '{0}{1}'.format(self.SUB_CONF_PREFIX, param.name)

if sub_conf_name in sub_confs:

category = sub_confs[sub_conf_name]

kwargs = {}
for param in category.params:

kwargs[param.name] = param.value

value = param.value(**kwargs)

self._configure(
toconfigure=value, logger=logger, conf=value
)


def getconfigurables(toconfigure):
Expand Down
11 changes: 9 additions & 2 deletions b3j0f/conf/model/param.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,14 +30,16 @@


from .base import ModelElement
from ..parser.core import parse, _getscope, ParserError, serialize
from ..parser.core import parse, ParserError, serialize

from six import string_types, reraise

from re import compile as re_compile

from collections import Iterable

from copy import deepcopy


class PType(object):
"""Dedicated to embed a specific type such as a parameter type in order to
Expand Down Expand Up @@ -349,7 +351,12 @@ def resolve(
if configurable is None: # init configurable
configurable = self.configurable

scope = _getscope(self.scope, scope)
if scope is None:
scope = self.scope

else:
scope, selfscope = deepcopy(self.scope), scope
scope.update(selfscope)

# parse value if str and if parser exists
try:
Expand Down
4 changes: 2 additions & 2 deletions b3j0f/conf/parser/__init__.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@

__all__ = [
'parse', 'ParserError', 'EXPR_PREFIX', 'serialize',
'resolve', 'ExprResolver', 'register', 'resolvernames'
'resolve', 'ExprResolver', 'register', 'names'
]

from .core import parse, ParserError, EXPR_PREFIX, serialize
from .exprres import resolve, ExprResolver, resolvernames, register
from .resolver import resolve, ExprResolver, names, register
23 changes: 12 additions & 11 deletions b3j0f/conf/parser/core.py
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@
A simple value is a string. It is possible to format a dedicated programming
language expression in respecting this syntax:
*%[{lang}]%{expr}%* where:
*%[{lang}]:{expr}%* where:
- lang is an optional expression evaluator name (``py`` for python, ``js`` for
javascript, etc.) Default is python.
Expand All @@ -70,9 +70,9 @@
:header: expression, result, description
"te%%'test'[:-2]%", test, 'te' + 'st' in default (python) evaluator
"te%js%'test'.substr(2)%", test, 'te' + 'st' in javascript evaluator
"test number %%2\%2%", "test number 0", "default evaluation of 2 modulo 2"
"te%:'test'[:-2]%", test, 'te' + 'st' in default (python) evaluator
"te%js:'test'.substr(2)%", test, 'te' + 'st' in javascript evaluator
"test number %:2\%2%", "test number 0", "default evaluation of 2 modulo 2"
Expression value
----------------
Expand All @@ -84,7 +84,7 @@
Such expression must respects this syntax:
**=[{lang}]:{expr} where:
*=[{lang}]:{expr}* where:
- expr is the expression to evaluate
- lang is the dedicated programming language keyword (for example, ``py`` for
Expand Down Expand Up @@ -159,17 +159,15 @@

from .resolver.registry import resolve

WORD = r'[a-zA-Z_]\w*'
EVAL_REF = r'@([^@]+\|)?(\w)?(\.*)?(\w)' #: ref parameter.

EVAL_REF = r'@([^@]+\|)?({0})?(\.+)?(+{0})?'.format(WORD) #: ref parameter.

EVAL_LOOKUP = r'%(\w+)%(.+)[^\\]%?' #: programmatic language expression.
EVAL_LOOKUP = r'%(\w):(.+)[^\\]%?' #: programmatic language expression.

EVAL_REGEX = '{0}|{1}'.format(EVAL_REF, EVAL_LOOKUP) #: all regex.

REGEX_COMP = re_compile(EVAL_REGEX) #: final regex compiler.

EXPR_PREFIX = r'=({0}):(.*)'.format(WORD) #: interpreted expression prefix
EXPR_PREFIX = r'=(\w):(.*)' #: interpreted expression prefix

EXPR_PREFIX_COMP = re_compile(EXPR_PREFIX) #: interpreted expression regex comp

Expand Down Expand Up @@ -212,7 +210,10 @@ def parse(
'false': False
}

if scope is not None:
if scope is None:
scope = default_scope

else:
scope.update(default_scope)

result = resolve(
Expand Down
10 changes: 10 additions & 0 deletions b3j0f/conf/parser/resolver/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
__all__ = [
'ExprResolver', 'ResolverRegistry', 'names', 'resolve', 'register',
'loadresolvers', 'defaultname', 'resolvejs', 'resolvepy'
]

from .base import ExprResolver
from .registry import (
ResolverRegistry, names, resolve, register, loadresolvers, defaultname
)
from .lang import resolvejs, resolvepy
7 changes: 4 additions & 3 deletions b3j0f/conf/parser/resolver/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,8 @@

__all__ = ['ExprResolver']

from six import add_metaclass

from .registry import register


Expand All @@ -57,20 +59,19 @@ class _MetaExprResolver(type):

def __new__(mcs, *args, **kwargs):

result = super(mcs, *args, **kwargs)
result = type.__new__(mcs, *args, **kwargs)

register(exprresolver=result)

return result


@add_metaclass(_MetaExprResolver)
class ExprResolver(object):
"""Expression resolver class.
All sub classes are automatically registered."""

__metaclass__ = _MetaExprResolver

__registry__ = 'py' #: regitration name.

def __call__(self, expr, safe=True, tostr=False, scope=None):
Expand Down
7 changes: 4 additions & 3 deletions b3j0f/conf/parser/resolver/lang/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
# SOFTWARE.
# --------------------------------------------------------------------

# extend the b3j0f.conf.parser.resolver.lang package
from pkgutil import extend_path
__path__ = extend_path(__path__, __name__)
__all__ = ['resolvejs', 'resolvepy']

from .js import resolvejs
from .py import resolvepy
38 changes: 27 additions & 11 deletions b3j0f/conf/parser/resolver/lang/js.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,25 +24,41 @@
# SOFTWARE.
# --------------------------------------------------------------------

from __future__ import print_function

"""Javascript expression resolver."""

__all__ = ['resolve']
__all__ = ['resolvejs']

from ..registry import register

from PyV8 import JSContext
try:
from PyV8 import JSContext

except ImportError:

from sys import stderr
print(
'Impossible to load the javascript resolver. Install the PyV8 before.',
file=stderr
)
def resolvejs(**_):
"""Default resolvejs if PyV8 is not installed."""
pass

else:
CTXT = JSContext()

CTXT = JSContext()
@register('js')
def resolvejs(expr, tostr=False, scope=None, **_):
"""Javascript resolver."""

@register('js')
def resolve(expr, tostr=False, scope=None, **_):
"""Javascript resolver."""
_ctxt = CTXT if scope is None else JSContext(scope)

_ctxt = CTXT if scope is None else JSContext(scope)
if tostr:
expr = '({0}).string'.format(expr)

if tostr:
expr = '({0}).string'.format(expr)
result = _ctxt.eval(expr)

result = _ctxt.eval(expr)
return result

return result
4 changes: 2 additions & 2 deletions b3j0f/conf/parser/resolver/lang/py.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,15 +26,15 @@

"""python expression resolver."""

__all__ = ['resolve']
__all__ = ['resolvepy']

from b3j0f.utils.runtime import safe_eval

from ..registry import register


@register('py')
def resolve(expr, safe=True, tostr=False, scope=None):
def resolvepy(expr, safe=True, tostr=False, scope=None):
"""Resolve input expression.
:param str expr: configuration expression to resolve in this language.
Expand Down

0 comments on commit 838abbf

Please sign in to comment.