--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/configurationengine/source/plugins/common/ConeLegacyRulePlugin/legacyruleplugin/rules.py Tue Aug 10 14:29:28 2010 +0300
@@ -0,0 +1,801 @@
+#
+# Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies).
+# All rights reserved.
+# This component and the accompanying materials are made available
+# under the terms of "Eclipse Public License v1.0"
+# which accompanies this distribution, and is available
+# at the URL "http://www.eclipse.org/legal/epl-v10.html".
+#
+# Initial Contributors:
+# Nokia Corporation - initial contribution.
+#
+# Contributors:
+#
+# Description:
+#
+
+import operator as ops
+import logging
+import tokenize
+from token import ENDMARKER, NAME, ERRORTOKEN
+import StringIO
+
+from cone.public import container
+
+RELATIONS = {}
+
+def abstract():
+ import inspect
+ caller = inspect.getouterframes(inspect.currentframe())[1][3]
+ raise NotImplementedError(caller + ' needs to be implemented')
+
+def get_tokens(tokenstr):
+ result = []
+ tokens = []
+ tokenstr = tokenstr.replace('\r', '')
+ name_buffer = [] # Temp buffer for reading name tokens
+ last_epos = None
+ for toknum, tokval, spos, epos, _ in tokenize.generate_tokens(StringIO.StringIO(unicode(tokenstr)).readline):
+ #print "toknum: %r, tokval: %r, spos: %r, epos: %r" % (toknum, tokval, spos, epos)
+ val = tokval.strip('\r\n\t ')
+
+ if toknum == ENDMARKER and name_buffer:
+ tokens.append(''.join(name_buffer))
+
+ # Ignore whitespace (this ignores also the end marker,
+ # since its value is empty)
+ if val == '': continue
+
+ # Put NAME, and ERRORTOKEN tokens through the temp
+ # buffer
+ if toknum in (NAME, ERRORTOKEN):
+ # If this and the previous token in the temp buffer are not adjacent,
+ # they belong to separate tokens
+ if name_buffer and spos[1] != last_epos[1]:
+ tokens.append(''.join(name_buffer))
+ name_buffer = []
+
+ name_buffer.append(val)
+ last_epos = epos
+ # Other tokens can just go directly to the token list
+ else:
+ if name_buffer:
+ tokens.append(''.join(name_buffer))
+ name_buffer = []
+ tokens.append(val)
+
+ while len(tokens) > 0:
+ val = tokens.pop(0)
+ # Join the refs with dot in between them to make them dotted refs
+ if val == '.':
+ newval = ".".join([result.pop(),tokens.pop(0)])
+ result.append( newval )
+ else:
+ result.append( val )
+
+ return result
+
+class RelationException(Exception):
+ pass
+
+#### The containers are here ####
+
+
+class RelationBase(object):
+ """
+ RelationBase defines a base class for all named relations that can be applied between objects.
+ e.g. Relation depends, that can be applied in Rule
+ """
+ relation_name = "RelationBase"
+ def __init__(self, data, left, right):
+ self.description = ""
+ self.data = data or container.DataContainer()
+ self.left = left
+ self.right = right
+
+ def __str__(self):
+ """
+ @return: A string presentation of the relation object
+ """
+ return "%s %s %s" % (self.left,self.relation_name,self.right)
+
+ def __repr__(self):
+ return "%s(ref=%r, lineno=%r)" % (self.__class__.__name__,
+ getattr(self, 'ref', None),
+ getattr(self, 'lineno', None))
+
+ def get_name(self):
+ """
+ @return: The relation name.
+ """
+ return self.relation_name
+
+ def get_description(self):
+ """
+ @return: a possible description of the relation.
+ """
+ return self.description
+
+ def execute(self):
+ """
+ Execute the relation object.
+ """
+ pass
+
+
+class RelationContainer(RelationBase, list):
+ """
+ This class provides the RelationContainer interface for collecting
+ relation sets into one place which can be executed through the container.
+ """
+ def __init__(self, data=None):
+ super(RelationContainer, self).__init__(data, 'LContainer', 'RContainer')
+ self.value_list = list()
+
+ def append(self, value):
+ self.value_list.append(value)
+
+ def __iter__(self):
+ return self.value_list.__iter__()
+
+ def __len__(self):
+ return len(self.value_list)
+
+ def __or__(self, other):
+ """
+ This function adds two RelationContainers to each other and removes
+ duplicates from the result.
+ The modification is inplace and the returned value is called object.
+ """
+ self.value_list = set(self.value_list) | set(other.value_list)
+ return self
+
+ def __unicode__(self):
+ if self:
+ ret = ''
+ for value in self:
+ ret += unicode(value)
+ else:
+ ret = 'No relations'
+ return ret
+
+ def find_relations(self, refs): abstract()
+ def add_relation(self, relation): abstract()
+ def has_errors(self): abstract()
+
+class RelationContainerImpl(RelationContainer):
+ """ Base implementation for RelationContainer to use in ConE rules
+ """
+ def execute(self):
+ ret = True
+ i = 0
+ for relation in self:
+ i += 1
+ r = relation.execute()
+ ret = ret and r
+ return ret
+
+ def find_relations(self, refs):
+ relations = []
+ for ref in refs:
+ for relation in self:
+ if relation.has_ref(ref):
+ relations.append(relation)
+ return relations
+
+ def add_relation(self, relation):
+ self.append(relation)
+
+ def has_ref(self, refs):
+ for ref in refs:
+ for relation in self:
+ if relation.has_ref(ref):
+ return True
+ return False
+
+ def has_errors(self):
+ for relation in self:
+ if relation.has_errors():
+ return True
+ return False
+
+ def get_errors(self):
+ errors = []
+ for relation in self:
+ errors += relation.get_errors()
+ return errors
+
+#### The relations are here ####
+
+class BaseRelation(RelationBase):
+ """ BaseRelation implements the basic evaluation logic for relations
+ This class abstract and should be extended for concrete implementation of
+ relation type.
+
+ Subclasses need to set their own context in their constructor before this
+ class's constructor is called if custom context is needed. If context not
+ set then DefaultContext is used.
+ """
+ KEY = 'base_relation'
+
+ def __init__(self, data, left, right):
+ # Context needs to be overridden for special purposes
+ try:
+ self.__getattribute__('context')
+ except AttributeError:
+ self.context = DefaultContext(data)
+
+ left = self.expand_rule_elements(left)
+ right = self.expand_rule_elements(right)
+ super(BaseRelation, self).__init__(data, left, right)
+ self.interpreter = ASTInterpreter(context=self.context)
+
+ def execute(self, context=None):
+ """
+ @return Returns error dictionary
+
+ In the client code proper way to check if the rule applies:
+ info = relation.execute()
+ if not info.has_errors():
+ else: HANDLE ERRORS
+ """
+ # logger.debug("Interpreter context %s" % self.interpreter.context)
+ self.interpreter.create_ast('%s %s %s' % (self.left, self.KEY, self.right))
+ ret = self.interpreter.eval(context, relation=self)
+ return ret
+
+ def get_keys(self):
+ """ Returns the references from this relation.
+ """
+ refs = ASTInterpreter.extract_refs(self.left)
+ refs += ASTInterpreter.extract_refs(self.right)
+ return refs
+
+ def has_ref(self, ref):
+ """ Returns if the 'ref' is included in this relation
+ """
+ return ref in self.get_keys()
+
+ def has_errors(self):
+ return bool(self.interpreter.errors)
+
+ def get_refs(self):
+ extracted_refs = ASTInterpreter.extract_refs(self.left)
+
+ # Handle eval expressions (a somewhat ugly hack,
+ # but this is a legacy plug-in so...)
+ interpreter = ASTInterpreter(self.left)
+ for expression in interpreter.parse_tree:
+ if hasattr(expression, 'extract_refs'):
+ extracted_refs.extend(expression.extract_refs())
+
+ # Filter out entries that evaluate successfully as Python code
+ # e.g. True or 123
+ result = []
+ for item in extracted_refs:
+ try: eval(item)
+ except: result.append(item)
+ else: pass
+ return result
+
+ def _eval_rside_value(self, value): abstract()
+ def _compare_value(self, value): abstract()
+
+ def extract_erroneus_features_with_values(self):
+ """
+ Extract references who have errors.
+
+ Returns dictionary { 'reference' : 'value' }
+ """
+ data_dict = {}
+ for ref in ASTInterpreter.extract_refs(self.right):
+ value = self.data.get_feature(ref)
+ if self._compare_value(value):
+ data_dict[ref] = value
+ elif value == None:
+ data_dict[ref] = None
+ return data_dict
+
+ def get_errors(self):
+ return self.interpreter.errors
+
+ def expand_rule_elements(self, rule):
+ """ Expans rule elements base on the reference.
+ Context is used for fetching the child elements for parent references
+ which uses asterisk identifier for selecting all child features:
+ 'parent_feature.*' -> 'child_fea_1 and child_fea_2'.
+ """
+ tokens = get_tokens(rule) # [token for token in rule.split()]
+
+ expanded_rule = ""
+ for token in tokens:
+ if token.endswith('.*'):
+ index = token.index('.*')
+ parent_ref = token[:index]
+ children = self.context.get_children_for_reference(parent_ref)
+ expanded_element = ' and '.join([child.reference for child in children])
+ if expanded_rule:
+ expanded_rule = '%s and %s' % (expanded_rule, expanded_element.rstrip())
+ else:
+ expanded_rule = expanded_element.rstrip()
+ elif token.lower() in OPERATORS:
+ expanded_rule += ' %s ' % token
+ else:
+ if expanded_rule:
+ expanded_rule += '%s'% token
+ else:
+ expanded_rule = token
+ return expanded_rule.strip()
+
+class RequireRelation(BaseRelation):
+ KEY = 'requires'
+RELATIONS[RequireRelation.KEY] = RequireRelation
+
+class ExcludesRelation(BaseRelation):
+ KEY = 'excludes'
+
+RELATIONS['excludes'] = ExcludesRelation
+
+################################
+# Abstract syntax tree builder #
+################################
+
+def nor(expression, a, b):
+ return not ops.or_(a, b)
+
+def nand(expression, a, b):
+ return not ops.and_(a, b)
+
+def truth_and(expression, a, b):
+ return ops.truth(a) and ops.truth(b)
+
+class DefaultContext(object):
+ """ DefaultContext implements ConE specific context for handling rules
+ """
+ def __init__(self, data):
+ self.data = data
+
+ def eval(self, ast, expression, value):
+ pass
+
+ def get_keys(self, refs):
+ return ASTInterpreter.extract_refs(refs)
+
+ def get_children_for_reference(self, reference):
+ # implement ConE specific children expansion
+ pass
+
+ def handle_terminal(self, expression):
+ try:
+ return int(expression)
+ except:
+ return expression
+
+PRECEDENCES = {
+ 'PREFIX_OPERATORS' : 10,
+ 'MULDIV_OPERATORS' : 8,
+ 'ADDSUB_OPERATORS' : 7,
+ 'SHIFT_OPERATORS' : 6,
+ 'BITWISE_OPERATORS' : 5,
+ 'COMPARISON_OPERATORS' : 4,
+ 'SET_OPERATORS' : 3,
+ 'BOOLEAN_OPERATORS' : 2,
+ 'RELATION_OPERATORS' : 1,
+ 'NOT_DEFINED' : 0
+}
+
+class Expression(object):
+ PRECEDENCE = PRECEDENCES['NOT_DEFINED']
+ KEY = 'base_expression'
+
+ def __init__(self, ast):
+ self.ast = ast
+ self.value = None
+
+ def get_title(self):
+ return self.KEY
+
+ def eval(self, context, **kwargs): pass
+
+class OneParamExpression(Expression):
+ PARAM_COUNT = 1
+ def __init__(self, ast, expression):
+ super(OneParamExpression, self).__init__(ast)
+ self.expression = expression
+
+ def __unicode__(self):
+ return u'%s %s' % (self.KEY, self.expression)
+
+ def eval(self, context, **kwargs):
+ self.value = self.OP(self.expression.eval(context, **kwargs))
+ context.eval(self.ast, self, self.value)
+ return self.value
+
+class TwoOperatorExpression(Expression):
+ PARAM_COUNT = 2
+ OP = None
+ EVAL_AS_BOOLS = True
+
+ def __init__(self, ast, left, right):
+ super(TwoOperatorExpression, self).__init__(ast)
+ self.left = left
+ self.right = right
+
+ def __unicode__(self):
+ return u'%s %s %s' % (self.left, self.KEY, self.right)
+
+ def eval(self, context, **kwargs):
+ self.value = self.OP(self.left.eval(context, **kwargs), self.right.eval(context, **kwargs))
+ context.eval(self.ast, self, self.value)
+ return self.value
+
+class TwoOperatorBooleanExpression(TwoOperatorExpression):
+ def eval(self, context, **kwargs):
+ self.value = self.OP(bool(self.left.eval(context, **kwargs)), bool(self.right.eval(context, **kwargs)))
+ context.eval(self.ast, self, self.value)
+ return self.value
+
+class TerminalExpression(Expression):
+ KEY = 'terminal'
+
+ def __init__(self, ast, expression):
+ super(TerminalExpression, self).__init__(ast)
+ self.expression = expression
+
+ def eval(self, context, **kwargs):
+ """ Use context to eval the value
+ Expression on TerminalExpression is feature reference or value
+ context should handle the reference conversion to correct value
+ """
+ from cone.public import exceptions
+ try:
+ context.configuration.get_default_view().get_feature(self.expression)
+ self.value = context.handle_terminal(self.expression)
+ except exceptions.NotFound:
+ self.value = context.convert_value(self.expression)
+
+ return self.value
+
+ def __unicode__(self):
+ return self.expression
+
+ def __repr__(self):
+ return self.expression
+
+ def get_ref(self):
+ """
+ @return: The setting reference, e.g. 'MyFeature.MySetting'
+ """
+ return self.expression
+
+
+class NegExpression(OneParamExpression):
+ PRECEDENCE = PRECEDENCES['PREFIX_OPERATORS']
+ KEY= '-'
+ OP = ops.neg
+
+class AndExpression(TwoOperatorBooleanExpression):
+ PRECEDENCE = PRECEDENCES['BOOLEAN_OPERATORS']
+ KEY= 'and'
+ OP = truth_and
+
+class NandExpression(TwoOperatorBooleanExpression):
+ PRECEDENCE = PRECEDENCES['BOOLEAN_OPERATORS']
+ KEY = 'nand'
+ OP = nand
+
+class OrExpression(TwoOperatorBooleanExpression):
+ PRECEDENCE = PRECEDENCES['BOOLEAN_OPERATORS']
+ KEY = 'or'
+ OP = ops.or_
+
+class XorExpression(TwoOperatorBooleanExpression):
+ PRECEDENCE = PRECEDENCES['BOOLEAN_OPERATORS']
+ KEY = 'xor'
+ OP = ops.xor
+
+class NorExpression(TwoOperatorBooleanExpression):
+ PRECEDENCE = PRECEDENCES['BOOLEAN_OPERATORS']
+ KEY = 'nor'
+ OP = nor
+
+class EqualExpression(TwoOperatorExpression):
+ PRECEDENCE = PRECEDENCES['COMPARISON_OPERATORS']
+ KEY = '=='
+ OP = ops.eq
+
+class NotEqualExpression(TwoOperatorExpression):
+ PRECEDENCE = PRECEDENCES['COMPARISON_OPERATORS']
+ KEY = '!='
+ OP = ops.ne
+
+class LessThanExpression(TwoOperatorExpression):
+ PRECEDENCE = PRECEDENCES['COMPARISON_OPERATORS']
+ KEY = '<'
+ OP = ops.lt
+
+class GreaterThanExpression(TwoOperatorExpression):
+ PRECEDENCE = PRECEDENCES['COMPARISON_OPERATORS']
+ KEY = '>'
+ OP = ops.gt
+
+class LessThanEqualExpression(TwoOperatorExpression):
+ PRECEDENCE = PRECEDENCES['COMPARISON_OPERATORS']
+ KEY = '<='
+ OP = ops.le
+
+class GreaterThanEqualExpression(TwoOperatorExpression):
+ PRECEDENCE = PRECEDENCES['COMPARISON_OPERATORS']
+ KEY = '>='
+ OP = ops.ge
+
+
+def handle_require(expression, left, right):
+ if left and right:
+ return True
+ elif not left:
+ return True
+ return False
+
+class RequireExpression(TwoOperatorExpression):
+ PRECEDENCE = PRECEDENCES['RELATION_OPERATORS']
+ KEY = 'requires'
+ OP = handle_require
+
+ def eval(self, context, **kwargs):
+ super(RequireExpression, self).eval(context, **kwargs)
+ if not self.value:
+ left_keys = []
+ for ref in self.ast.extract_refs(unicode(self.left)):
+ for key in context.get_keys(ref):
+ left_keys.append(key)
+
+ for key in left_keys:
+ self.ast.add_error(key, { 'error_string' : 'REQUIRES right side value is "False"',
+ 'left_key' : key,
+ 'rule' : self.ast.expression
+ })
+ return self.value
+
+def handle_exclude(expression, left, right):
+ if left and not right:
+ return True
+ elif not left:
+ return True
+ return False
+
+class ExcludeExpression(TwoOperatorExpression):
+ PRECEDENCE = PRECEDENCES['RELATION_OPERATORS']
+ KEY = 'excludes'
+ OP = handle_exclude
+
+ def eval(self, context, **kwargs):
+ super(ExcludeExpression, self).eval(context, **kwargs)
+ if not self.value:
+ left_keys = []
+ for ref in self.ast.extract_refs(unicode(self.left)):
+ for key in context.get_keys(ref):
+ left_keys.append(key)
+
+ for key in left_keys:
+ self.ast.add_error(key, { 'error_string' : 'EXCLUDE right side value is "True"',
+ 'left_key' : key,
+ 'rule' : self.ast.expression
+ })
+ return self.value
+
+
+class NotExpression(OneParamExpression):
+ PRECEDENCE = PRECEDENCES['PREFIX_OPERATORS']
+ KEY = 'not'
+ OP = ops.not_
+
+class TruthExpression(OneParamExpression):
+ PRECEDENCE = PRECEDENCES['PREFIX_OPERATORS']
+ KEY = 'truth'
+ OP = ops.truth
+
+LEFT_PARENTHESIS = '('
+RIGHT_PARENTHESIS = ')'
+class SimpleCondition(EqualExpression):
+ """
+ A simple condition object that can refer to a model object and evaluate if the value matches
+ """
+ def __init__(self, left, right):
+ lterm = TerminalExpression(None, left)
+ rterm = TerminalExpression(None, right)
+ EqualExpression.__init__(self, None, lterm, rterm)
+
+
+# in format KEY : OPERATOR CLASS
+OPERATORS = {
+ 'and' : AndExpression,
+ 'nand' : NandExpression,
+ 'or' : OrExpression,
+ 'xor' : XorExpression,
+ 'nor' : NorExpression,
+ 'not' : NotExpression,
+ 'truth' : TruthExpression,
+ '==' : EqualExpression,
+ '!=' : NotEqualExpression,
+ '<' : LessThanExpression,
+ '>' : GreaterThanExpression,
+ '<=' : LessThanEqualExpression,
+ '>=' : GreaterThanEqualExpression,
+ 'requires' : RequireExpression,
+ 'excludes' : ExcludeExpression,
+ '-' : NegExpression
+ }
+
+def add_operator(key, operator_class=None, baseclass=RequireExpression):
+ """
+ Add new operator key and operator class.
+ If operator class isn't provided the baseclass parameter is used as
+ operator base. The baseclass parameter is RequireExpression by default
+ which has success condition left_rule=True and right_rule=True
+
+ """
+ OPERATORS[key] = operator_class or create_new_class(key, baseclass)
+
+def create_new_class(key, baseclass):
+ ns = baseclass.__dict__.copy()
+ ns['KEY'] = key
+ key_pieces = key.split('_')
+ class_prefix = ''.join([key_piece.capitalize() for key_piece in key_pieces])
+ new_class = type(class_prefix + 'Expression', (baseclass,), ns)
+ return new_class
+
+class ParseException(Exception): pass
+
+class ASTInterpreter(object):
+ def __init__(self, infix_expression=None, context=None):
+ """ Takes infix expression as string """
+ self.context = context or DefaultContext(None)
+ # logger.debug("AST init context: %s" % self.context)
+ self._init_locals(infix_expression)
+ if infix_expression:
+ self.create_ast()
+
+ def _init_locals(self, infix_expression):
+ # The result value of full eval of the parse_tree
+ self.value = None
+ self.warnings = {}
+ self.errors = {}
+ self.postfix_array = []
+ self.parse_tree = []
+ self.expression = infix_expression
+
+ def __unicode__(self):
+ s = ''
+ for expr in self.parse_tree:
+ s += unicode(expr)
+ return s
+
+ def add_error(self, key, error_dict):
+ if self.errors.has_key(key):
+ self.errors[key].append(error_dict)
+ else:
+ self.errors[key] = [error_dict]
+
+ def create_ast(self, infix_expression=None):
+ if infix_expression:
+ self._init_locals(infix_expression)
+ self._infix_to_postfix()
+ self._create_parse_tree()
+ return self.parse_tree
+
+ def _infix_to_postfix(self):
+ """
+ Shunting yard algorithm used to convert infix presentation to postfix.
+ """
+ if not self.expression:
+ raise ParseException('Expression is None')
+ tokens = get_tokens(self.expression) # [token for token in self.expression.split()]
+ stack = []
+ # logger.debug('TOKENS: %s' % tokens)
+ for token in tokens:
+ # logger.debug('TOKEN: %s' % token)
+ if token.lower() in OPERATORS:
+ op_class = OPERATORS.get(token)
+ if stack:
+ while len(stack) != 0:
+ top = stack[-1]
+ if top in OPERATORS:
+ top_operator = OPERATORS.get(top)
+ if op_class.PRECEDENCE <= top_operator.PRECEDENCE:
+ self.postfix_array.append(stack.pop())
+ else:
+ # Break from loop if top operator precedence is less.
+ break
+ else:
+ # If top not operator break from loop
+ break
+ stack.append(token)
+ elif token == LEFT_PARENTHESIS:
+ # logger.debug('Left parenthesis')
+ stack.append(token)
+ elif token == RIGHT_PARENTHESIS:
+ # logger.debug('Right parenthesis')
+ left_par_found = False
+ stack_token = stack.pop()
+ while stack_token:
+ if stack_token != LEFT_PARENTHESIS:
+ self.postfix_array.append(stack_token)
+ else:
+ left_par_found = True
+ break
+ if stack:
+ stack_token = stack.pop()
+ else:
+ stack_token = None
+
+ if not left_par_found:
+ raise ParseException('Mismatched parenthesis "%s".' % LEFT_PARENTHESIS)
+ else:
+ # logger.debug('Adding value to output. %s' % repr((token)))
+ self.postfix_array.append((token))
+
+ # There should be only operators left in the stack
+ if stack:
+ # logger.debug('Operators in stack.')
+ operator = stack.pop()
+ while operator:
+ if operator != LEFT_PARENTHESIS:
+ self.postfix_array.append(operator)
+ else:
+ raise ParseException('Mismatched parenthesis "%s".' % LEFT_PARENTHESIS)
+ if stack:
+ operator = stack.pop()
+ else:
+ operator = None
+
+ # logger.debug('Infix to postfix conversion: %s' % self.postfix_array)
+ return self.postfix_array
+
+ def _create_parse_tree(self):
+ self.parse_tree = []
+ for token in self.postfix_array:
+ if token in OPERATORS:
+ # logger.debug('OP: %s' % (token))
+ expression_class = OPERATORS[token]
+ params = []
+ for i in range(expression_class.PARAM_COUNT):
+ try:
+ params.append(self.parse_tree.pop())
+ except IndexError, e:
+ raise ParseException('Syntax error: "%s"' % self.expression)
+ params.reverse()
+ expression = expression_class(self, *params)
+
+ # logger.debug('The operation: %s' % expression)
+ self.parse_tree.append(expression)
+ else:
+ expression = TerminalExpression(self, token)
+ self.parse_tree.append(expression)
+
+ #logger.debug('THE STACK: %s' % self.parse_tree)
+ #for s in self.parse_tree:
+ # logger.debug('Stack e: %s' % str(s))
+
+ return self.parse_tree
+
+ def eval(self, context=None, **kwargs):
+ """ Evals the AST
+ If empty expression is given, None is returned
+ """
+ for expression in self.parse_tree:
+ self.value = expression.eval(context, **kwargs)
+ return self.value
+
+ @staticmethod
+ def extract_refs(expression):
+ tokens = get_tokens(expression)
+ refs = []
+ for token in tokens:
+ if not token.lower() in OPERATORS and token != LEFT_PARENTHESIS and token != RIGHT_PARENTHESIS:
+ refs.append(token.strip('%s%s' % (LEFT_PARENTHESIS, RIGHT_PARENTHESIS)))
+ return refs
+
+##################################################################
+# Create and configure the main level logger
+logger = logging.getLogger('cone')