""" ``lexer`` module
"""
import re
import os.path
from warnings import warn
from wheezy.html.ext.parser import parse_known_function
from wheezy.html.ext.parser import parse_name
from wheezy.html.ext.parser import parse_params
from wheezy.html.ext.parser import parse_str_or_int
from wheezy.html.utils import html_id
class Preprocessor(object):
[docs] """ Generic widget preprocessor.
"""
CHECKBOX = None
ERROR = None
ERROR_CLASS0 = None
ERROR_CLASS1 = None
EXPRESSION = None
HIDDEN = '<input type="hidden" name="%(name)s" value="%(value)s" />'
INPUT = None
LABEL = '<label for="%(id)s"%(attrs)s%(class)s>%(value)s</label>'
MESSAGE = None
MULTIPLE_CHECKBOX = None
MULTIPLE_HIDDEN = None
PREPEND = None
RADIO = None
SELECT = None
TEXTAREA = '<textarea id="%(id)s" name="%(name)s"%(attrs)s%(class)s>' \
'%(value)s</textarea>'
# region: preprocessing
def __init__(self, widgets_pattern):
self.widgets = {
'checkbox': self.checkbox,
'dropdown': self.dropdown,
'emptybox': self.emptybox,
'error': self.error,
'hidden': self.hidden,
'info': self.info,
'label': self.label,
'listbox': self.listbox,
'multiple_checkbox': self.multiple_checkbox,
'multiple_hidden': self.multiple_hidden,
'multiple_select': self.listbox,
'password': self.password,
'radio': self.radio,
'select': self.dropdown,
'textarea': self.textarea,
'textbox': self.textbox,
'warning': self.warning,
}
assert '%(widgets)s' in widgets_pattern
self.RE_WIDGETS = re.compile(widgets_pattern % {
'widgets': '|'.join(self.widgets.keys())})
def __call__(self, text, **kwargs):
""" Preprocess input text.
"""
result = []
start = 0
for m in self.RE_WIDGETS.finditer(text):
result.append(text[start:m.start()])
start = m.end()
args = m.groupdict()
widget = self.widgets[args.pop('widget')]
result.append(widget(**args))
if start > 0 and self.PREPEND:
result.insert(0, self.PREPEND)
result.append(text[start:])
return ''.join(result)
# region: helpers
def expression(self, text, expr_filter=''):
[docs] """ Interpretate ``text`` as string expression or
python expression.
"""
value = parse_str_or_int(text)
return value or self.EXPRESSION % {
'expr': text,
'expr_filter': expr_filter}
def join_attrs(self, kwargs):
[docs] """ Joins ``kwargs`` as html attributes.
"""
if kwargs:
return ' ' + ' '.join([
'%s="%s"' % (k, self.expression(kwargs[k]))
for k in sorted(kwargs.keys())])
else:
return ''
def error_class(self, name, class_):
[docs] """ Checks for error and add css class error.
"""
if class_:
return self.ERROR_CLASS1 % {
'name': name,
'class': self.expression(class_)}
else:
return self.ERROR_CLASS0 % {
'name': name}
# region: widgets
def hidden(self, expr, params, expr_filter):
[docs] """ HTML element input hidden.
"""
name = parse_name(expr)
return self.HIDDEN % {
'name': name,
'value': self.expression(expr, expr_filter)}
def multiple_hidden(self, expr, params, expr_filter):
[docs] """ Multiple HTML element input of type hidden.
"""
name = parse_name(expr)
return self.MULTIPLE_HIDDEN % {
'name': name,
'value': expr,
'expr_filter': expr_filter}
def label(self, expr, params, expr_filter):
[docs] """ HTML element label.
"""
name = parse_name(expr)
args, kwargs = parse_params(params)
class_ = kwargs.pop('class', None)
return self.LABEL % {
'id': html_id(name),
'name': name,
'value': self.expression(args[0], expr_filter),
'attrs': self.join_attrs(kwargs),
'class': self.error_class(name, class_)}
def emptybox(self, expr, params, expr_filter):
[docs] """ HTML element input of type text. Value is rendered
only if evaluated to boolean True.
"""
return self.input_helper(expr, params, expr_filter, 'empty')
def textbox(self, expr, params, expr_filter):
[docs] """ HTML element input of type text. Value is rendered
only if it is not None or ''.
"""
return self.input_helper(expr, params, expr_filter, 'text')
def password(self, expr, params, expr_filter):
[docs] """ HTML element input of type password. Value is rendered
only if it is not None or ''.
"""
return self.input_helper(expr, params, expr_filter, 'password')
def input_helper(self, expr, params, expr_filter, input_type):
[docs] """ HTML element textarea.
"""
name = parse_name(expr)
args, kwargs = parse_params(params)
kwargs.setdefault('rows', '"9"')
kwargs.setdefault('cols', '"40"')
class_ = kwargs.pop('class', None)
return self.TEXTAREA % {
'id': html_id(name),
'name': name,
'value': self.expression(expr, expr_filter),
'attrs': self.join_attrs(kwargs),
'class': self.error_class(name, class_)}
def checkbox(self, expr, params, expr_filter):
[docs] """ HTML element input of type checkbox.
"""
name = parse_name(expr)
args, kwargs = parse_params(params)
class_ = kwargs.pop('class', None)
return self.CHECKBOX % {
'id': html_id(name),
'name': name,
'value': expr,
'attrs': self.join_attrs(kwargs),
'class': self.error_class(name, class_)}
def multiple_checkbox(self, expr, params, expr_filter):
[docs] """ Multiple HTML element input of type checkbox.
"""
name = parse_name(expr)
args, kwargs = parse_params(params)
choices = kwargs.pop('choices')
class_ = kwargs.pop('class', None)
return self.MULTIPLE_CHECKBOX % {
'id': html_id(name),
'name': name,
'choices': choices,
'value': expr,
'expr_filter': expr_filter,
'attrs': self.join_attrs(kwargs),
'class': self.error_class(name, class_)}
def radio(self, expr, params, expr_filter):
[docs] """ A group of HTML input elements of type radio.
"""
name = parse_name(expr)
args, kwargs = parse_params(params)
class_ = kwargs.pop('class', None)
choices = kwargs.pop('choices')
return self.RADIO % {
'id': html_id(name),
'name': name,
'choices': choices,
'value': expr,
'expr_filter': expr_filter,
'attrs': self.join_attrs(kwargs),
'class': self.error_class(name, class_)}
def dropdown(self, expr, params, expr_filter):
[docs] """ HTML element select.
"""
name = parse_name(expr)
args, kwargs = parse_params(params)
class_ = kwargs.pop('class', None)
choices = kwargs.pop('choices')
return self.SELECT % {
'id': html_id(name),
'name': name,
'choices': choices,
'value': expr,
'expr_filter': expr_filter,
'attrs': self.join_attrs(kwargs),
'class': self.error_class(name, class_)}
def listbox(self, expr, params, expr_filter):
[docs] """ HTML element select of type multiple.
"""
name = parse_name(expr)
args, kwargs = parse_params(params)
class_ = kwargs.pop('class', None)
choices = kwargs.pop('choices')
return self.MULTIPLE_SELECT % {
'id': html_id(name),
'name': name,
'choices': choices,
'value': expr,
'expr_filter': expr_filter,
'attrs': self.join_attrs(kwargs),
'class': self.error_class(name, class_)}
def error(self, expr, params, expr_filter):
[docs] """ General error message or field error.
"""
name = parse_name(expr)
args, kwargs = parse_params(params)
class_ = kwargs.pop('class', '')
if class_:
class_ = ' ' + self.expression(class_)
if '.' not in expr:
name = '__ERROR__'
kwargs['class'] = '"error-message' + class_ + '"'
else:
kwargs['class'] = '"error' + class_ + '"'
return self.ERROR % {
'name': name,
'attrs': self.join_attrs(kwargs),
'expr_filter': expr_filter}
def info(self, expr, params, expr_filter):
[docs] """ General info message.
"""
return self.message_helper(expr, params, expr_filter, 'info')
def warning(self, expr, params, expr_filter):
[docs] """ General warning message.
"""
return self.message_helper(expr, params, expr_filter, 'warning')
def message_helper(self, expr, params, expr_filter, msg_class):
[docs] """ General info message.
"""
args, kwargs = parse_params(params)
class_ = kwargs.pop('class', '')
if class_:
class_ = ' ' + self.expression(class_)
if '.' not in expr:
class_ = '-message' + class_
kwargs['class'] = '"' + msg_class + class_ + '"'
return self.MESSAGE % {
'value': expr,
'info': self.expression(expr, expr_filter),
'attrs': self.join_attrs(kwargs)}
class WhitespacePreprocessor(object):
[docs] """ Whitespace preprocessor.
"""
def __init__(self, rules, ignore_rules=None):
self.rules = rules
self.ignore_rules = ignore_rules
def __call__(self, text, **kwargs):
if self.ignore_rules:
for ignore_rule in self.ignore_rules:
start = 0
result = []
for m in ignore_rule.finditer(text):
result.append(self.cleanup(text[start:m.start()]))
result.append(m.group())
start = m.end()
else:
result.append(self.cleanup(text[start:]))
text = ''.join(result)
return text
else:
return self.cleanup(text)
def cleanup(self, text):
for r, s in self.rules:
text = r.sub(s, text)
return text
class InlinePreprocessor(object):
[docs] """ Inline preprocessor
"""
def __init__(self, pattern, directories, strategy=None):
self.pattern = pattern
self.directories = directories
if strategy:
self.strategy = strategy
def __call__(self, text, **kwargs):
result = []
start = 0
for m in self.pattern.finditer(text):
result.append(text[start:m.start()])
start = m.end()
path = m.group('path')
result.append(self(self.strategy(path)))
if start:
result.append(text[start:])
return ''.join(result)
else:
return text
def strategy(self, path):
path = path.lstrip('/')
for d in self.directories:
abspath = os.path.abspath(os.path.join(d, path))
if os.path.exists(abspath) and os.path.isfile(abspath):
f = open(abspath, 'r')
try:
return f.read()
finally:
f.close()
warn('InlinePreprocessor: "%s" not found.' % path)
return ''