Fixed an issue that prevented the password reset tokens from working. Added email templates for password reset success and new account creation. Added more dynamic email template support.
133 lines
4.8 KiB
Python
133 lines
4.8 KiB
Python
#-----------------------------------------------------------------
|
|
# plyparser.py
|
|
#
|
|
# PLYParser class and other utilities for simplifying programming
|
|
# parsers with PLY
|
|
#
|
|
# Eli Bendersky [https://eli.thegreenplace.net/]
|
|
# License: BSD
|
|
#-----------------------------------------------------------------
|
|
|
|
import warnings
|
|
|
|
class Coord(object):
|
|
""" Coordinates of a syntactic element. Consists of:
|
|
- File name
|
|
- Line number
|
|
- (optional) column number, for the Lexer
|
|
"""
|
|
__slots__ = ('file', 'line', 'column', '__weakref__')
|
|
def __init__(self, file, line, column=None):
|
|
self.file = file
|
|
self.line = line
|
|
self.column = column
|
|
|
|
def __str__(self):
|
|
str = "%s:%s" % (self.file, self.line)
|
|
if self.column: str += ":%s" % self.column
|
|
return str
|
|
|
|
|
|
class ParseError(Exception): pass
|
|
|
|
|
|
class PLYParser(object):
|
|
def _create_opt_rule(self, rulename):
|
|
""" Given a rule name, creates an optional ply.yacc rule
|
|
for it. The name of the optional rule is
|
|
<rulename>_opt
|
|
"""
|
|
optname = rulename + '_opt'
|
|
|
|
def optrule(self, p):
|
|
p[0] = p[1]
|
|
|
|
optrule.__doc__ = '%s : empty\n| %s' % (optname, rulename)
|
|
optrule.__name__ = 'p_%s' % optname
|
|
setattr(self.__class__, optrule.__name__, optrule)
|
|
|
|
def _coord(self, lineno, column=None):
|
|
return Coord(
|
|
file=self.clex.filename,
|
|
line=lineno,
|
|
column=column)
|
|
|
|
def _token_coord(self, p, token_idx):
|
|
""" Returns the coordinates for the YaccProduction object 'p' indexed
|
|
with 'token_idx'. The coordinate includes the 'lineno' and
|
|
'column'. Both follow the lex semantic, starting from 1.
|
|
"""
|
|
last_cr = p.lexer.lexer.lexdata.rfind('\n', 0, p.lexpos(token_idx))
|
|
if last_cr < 0:
|
|
last_cr = -1
|
|
column = (p.lexpos(token_idx) - (last_cr))
|
|
return self._coord(p.lineno(token_idx), column)
|
|
|
|
def _parse_error(self, msg, coord):
|
|
raise ParseError("%s: %s" % (coord, msg))
|
|
|
|
|
|
def parameterized(*params):
|
|
""" Decorator to create parameterized rules.
|
|
|
|
Parameterized rule methods must be named starting with 'p_' and contain
|
|
'xxx', and their docstrings may contain 'xxx' and 'yyy'. These will be
|
|
replaced by the given parameter tuples. For example, ``p_xxx_rule()`` with
|
|
docstring 'xxx_rule : yyy' when decorated with
|
|
``@parameterized(('id', 'ID'))`` produces ``p_id_rule()`` with the docstring
|
|
'id_rule : ID'. Using multiple tuples produces multiple rules.
|
|
"""
|
|
def decorate(rule_func):
|
|
rule_func._params = params
|
|
return rule_func
|
|
return decorate
|
|
|
|
|
|
def template(cls):
|
|
""" Class decorator to generate rules from parameterized rule templates.
|
|
|
|
See `parameterized` for more information on parameterized rules.
|
|
"""
|
|
issued_nodoc_warning = False
|
|
for attr_name in dir(cls):
|
|
if attr_name.startswith('p_'):
|
|
method = getattr(cls, attr_name)
|
|
if hasattr(method, '_params'):
|
|
# Remove the template method
|
|
delattr(cls, attr_name)
|
|
# Create parameterized rules from this method; only run this if
|
|
# the method has a docstring. This is to address an issue when
|
|
# pycparser's users are installed in -OO mode which strips
|
|
# docstrings away.
|
|
# See: https://github.com/eliben/pycparser/pull/198/ and
|
|
# https://github.com/eliben/pycparser/issues/197
|
|
# for discussion.
|
|
if method.__doc__ is not None:
|
|
_create_param_rules(cls, method)
|
|
elif not issued_nodoc_warning:
|
|
warnings.warn(
|
|
'parsing methods must have __doc__ for pycparser to work properly',
|
|
RuntimeWarning,
|
|
stacklevel=2)
|
|
issued_nodoc_warning = True
|
|
return cls
|
|
|
|
|
|
def _create_param_rules(cls, func):
|
|
""" Create ply.yacc rules based on a parameterized rule function
|
|
|
|
Generates new methods (one per each pair of parameters) based on the
|
|
template rule function `func`, and attaches them to `cls`. The rule
|
|
function's parameters must be accessible via its `_params` attribute.
|
|
"""
|
|
for xxx, yyy in func._params:
|
|
# Use the template method's body for each new method
|
|
def param_rule(self, p):
|
|
func(self, p)
|
|
|
|
# Substitute in the params for the grammar rule and function name
|
|
param_rule.__doc__ = func.__doc__.replace('xxx', xxx).replace('yyy', yyy)
|
|
param_rule.__name__ = func.__name__.replace('xxx', xxx)
|
|
|
|
# Attach the new method to the class
|
|
setattr(cls, param_rule.__name__, param_rule)
|