2014-10-25 01:24:05 +00:00
|
|
|
import operator
|
|
|
|
import os
|
2012-10-28 01:15:29 +00:00
|
|
|
import abc
|
2015-05-02 22:11:51 +00:00
|
|
|
import pyparsing as pp
|
2012-07-24 00:46:14 +00:00
|
|
|
|
2015-05-02 04:17:00 +00:00
|
|
|
from .. import utils
|
|
|
|
from . import generators, exceptions
|
2012-04-28 00:42:03 +00:00
|
|
|
|
2012-06-24 05:01:04 +00:00
|
|
|
|
2015-05-02 09:42:09 +00:00
|
|
|
Sep = pp.Optional(pp.Literal(":")).suppress()
|
|
|
|
|
|
|
|
|
2014-10-25 04:27:08 +00:00
|
|
|
v_integer = pp.Word(pp.nums)\
|
2012-04-28 00:42:03 +00:00
|
|
|
.setName("integer")\
|
|
|
|
.setParseAction(lambda toks: int(toks[0]))
|
|
|
|
|
2012-04-28 05:12:39 +00:00
|
|
|
|
|
|
|
v_literal = pp.MatchFirst(
|
2012-04-28 00:42:03 +00:00
|
|
|
[
|
2014-10-25 01:24:05 +00:00
|
|
|
pp.QuotedString(
|
|
|
|
"\"",
|
|
|
|
unquoteResults=True,
|
|
|
|
multiline=True
|
|
|
|
),
|
|
|
|
pp.QuotedString(
|
|
|
|
"'",
|
|
|
|
unquoteResults=True,
|
|
|
|
multiline=True
|
|
|
|
),
|
2012-04-28 00:42:03 +00:00
|
|
|
]
|
|
|
|
)
|
|
|
|
|
2012-04-28 05:12:39 +00:00
|
|
|
v_naked_literal = pp.MatchFirst(
|
2012-04-28 00:42:03 +00:00
|
|
|
[
|
2012-04-28 05:12:39 +00:00
|
|
|
v_literal,
|
2012-10-30 20:48:55 +00:00
|
|
|
pp.Word("".join(i for i in pp.printables if i not in ",:\n@\'\""))
|
2012-04-28 00:42:03 +00:00
|
|
|
]
|
|
|
|
)
|
|
|
|
|
|
|
|
|
2015-05-02 20:02:13 +00:00
|
|
|
class Token(object):
|
2012-10-29 23:36:38 +00:00
|
|
|
"""
|
2015-05-03 00:53:28 +00:00
|
|
|
A token in the specification language. Tokens are immutable. The token
|
|
|
|
classes have no meaning in and of themselves, and are combined into
|
|
|
|
Components and Actions to build the language.
|
2012-10-29 23:36:38 +00:00
|
|
|
"""
|
2012-10-28 09:00:19 +00:00
|
|
|
__metaclass__ = abc.ABCMeta
|
2014-10-25 01:24:05 +00:00
|
|
|
|
2015-04-19 20:56:47 +00:00
|
|
|
@classmethod
|
2012-10-29 23:36:38 +00:00
|
|
|
def expr(klass): # pragma: no cover
|
|
|
|
"""
|
|
|
|
A parse expression.
|
|
|
|
"""
|
2012-10-28 09:00:19 +00:00
|
|
|
return None
|
|
|
|
|
2012-10-30 20:32:21 +00:00
|
|
|
@abc.abstractmethod
|
2012-10-29 23:36:38 +00:00
|
|
|
def spec(self): # pragma: no cover
|
|
|
|
"""
|
|
|
|
A parseable specification for this token.
|
|
|
|
"""
|
2012-10-28 09:00:19 +00:00
|
|
|
return None
|
|
|
|
|
2015-04-28 22:02:16 +00:00
|
|
|
def resolve(self, settings, msg):
|
2012-10-30 01:46:18 +00:00
|
|
|
"""
|
|
|
|
Resolves this token to ready it for transmission. This means that
|
|
|
|
the calculated offsets of actions are fixed.
|
2015-05-02 20:51:57 +00:00
|
|
|
|
|
|
|
settings: a language.Settings instance
|
|
|
|
msg: The containing message
|
2012-10-30 01:46:18 +00:00
|
|
|
"""
|
|
|
|
return self
|
|
|
|
|
2012-10-29 23:36:38 +00:00
|
|
|
def __repr__(self):
|
|
|
|
return self.spec()
|
|
|
|
|
2012-10-28 09:00:19 +00:00
|
|
|
|
2015-05-03 00:53:28 +00:00
|
|
|
class _TokValueLiteral(Token):
|
2012-04-28 00:42:03 +00:00
|
|
|
def __init__(self, val):
|
2012-07-21 02:12:45 +00:00
|
|
|
self.val = val.decode("string_escape")
|
2012-04-28 00:42:03 +00:00
|
|
|
|
|
|
|
def get_generator(self, settings):
|
2015-05-02 20:51:57 +00:00
|
|
|
return self.val
|
2012-04-28 00:42:03 +00:00
|
|
|
|
2012-10-30 20:32:21 +00:00
|
|
|
def freeze(self, settings):
|
|
|
|
return self
|
|
|
|
|
2012-06-28 22:42:15 +00:00
|
|
|
|
2015-05-03 00:53:28 +00:00
|
|
|
class TokValueLiteral(_TokValueLiteral):
|
2015-05-02 22:11:51 +00:00
|
|
|
"""
|
|
|
|
A literal with Python-style string escaping
|
|
|
|
"""
|
2012-04-28 00:42:03 +00:00
|
|
|
@classmethod
|
|
|
|
def expr(klass):
|
|
|
|
e = v_literal.copy()
|
2014-10-26 03:27:25 +00:00
|
|
|
return e.setParseAction(klass.parseAction)
|
|
|
|
|
|
|
|
@classmethod
|
|
|
|
def parseAction(klass, x):
|
|
|
|
v = klass(*x)
|
|
|
|
return v
|
2012-04-28 00:42:03 +00:00
|
|
|
|
2012-10-23 22:32:53 +00:00
|
|
|
def spec(self):
|
2015-05-02 22:11:51 +00:00
|
|
|
inner = self.val.encode("string_escape")
|
|
|
|
inner = inner.replace(r"\'", r"\x27")
|
|
|
|
return "'" + inner + "'"
|
2012-10-23 22:32:53 +00:00
|
|
|
|
2012-06-28 22:42:15 +00:00
|
|
|
|
2015-05-03 00:53:28 +00:00
|
|
|
class TokValueNakedLiteral(_TokValueLiteral):
|
2012-06-28 22:42:15 +00:00
|
|
|
@classmethod
|
|
|
|
def expr(klass):
|
|
|
|
e = v_naked_literal.copy()
|
|
|
|
return e.setParseAction(lambda x: klass(*x))
|
2012-04-28 00:42:03 +00:00
|
|
|
|
2012-10-23 22:32:53 +00:00
|
|
|
def spec(self):
|
|
|
|
return self.val.encode("string_escape")
|
|
|
|
|
2012-04-28 00:42:03 +00:00
|
|
|
|
2015-05-03 00:53:28 +00:00
|
|
|
class TokValueGenerate(Token):
|
2012-04-28 00:42:03 +00:00
|
|
|
def __init__(self, usize, unit, datatype):
|
|
|
|
if not unit:
|
|
|
|
unit = "b"
|
|
|
|
self.usize, self.unit, self.datatype = usize, unit, datatype
|
|
|
|
|
|
|
|
def bytes(self):
|
2012-07-23 03:03:56 +00:00
|
|
|
return self.usize * utils.SIZE_UNITS[self.unit]
|
2012-04-28 00:42:03 +00:00
|
|
|
|
|
|
|
def get_generator(self, settings):
|
2015-05-02 04:17:00 +00:00
|
|
|
return generators.RandomGenerator(self.datatype, self.bytes())
|
2012-04-28 00:42:03 +00:00
|
|
|
|
2012-10-30 20:32:21 +00:00
|
|
|
def freeze(self, settings):
|
|
|
|
g = self.get_generator(settings)
|
2015-05-03 00:53:28 +00:00
|
|
|
return TokValueLiteral(g[:].encode("string_escape"))
|
2012-10-30 20:32:21 +00:00
|
|
|
|
2012-04-28 00:42:03 +00:00
|
|
|
@classmethod
|
|
|
|
def expr(klass):
|
2012-04-29 02:20:27 +00:00
|
|
|
e = pp.Literal("@").suppress() + v_integer
|
2012-04-28 00:42:03 +00:00
|
|
|
|
2014-10-25 01:24:05 +00:00
|
|
|
u = reduce(
|
|
|
|
operator.or_,
|
|
|
|
[pp.Literal(i) for i in utils.SIZE_UNITS.keys()]
|
2014-10-25 04:27:08 +00:00
|
|
|
).leaveWhitespace()
|
2012-04-28 00:42:03 +00:00
|
|
|
e = e + pp.Optional(u, default=None)
|
|
|
|
|
2012-04-29 02:20:27 +00:00
|
|
|
s = pp.Literal(",").suppress()
|
2015-05-02 04:17:00 +00:00
|
|
|
s += reduce(
|
|
|
|
operator.or_,
|
|
|
|
[pp.Literal(i) for i in generators.DATATYPES.keys()]
|
|
|
|
)
|
2012-04-28 00:42:03 +00:00
|
|
|
e += pp.Optional(s, default="bytes")
|
|
|
|
return e.setParseAction(lambda x: klass(*x))
|
|
|
|
|
2012-10-23 22:32:53 +00:00
|
|
|
def spec(self):
|
|
|
|
s = "@%s"%self.usize
|
|
|
|
if self.unit != "b":
|
|
|
|
s += self.unit
|
|
|
|
if self.datatype != "bytes":
|
|
|
|
s += ",%s"%self.datatype
|
|
|
|
return s
|
|
|
|
|
2012-04-28 00:42:03 +00:00
|
|
|
|
2015-05-03 00:53:28 +00:00
|
|
|
class TokValueFile(Token):
|
2012-04-28 00:42:03 +00:00
|
|
|
def __init__(self, path):
|
2012-10-29 04:33:10 +00:00
|
|
|
self.path = str(path)
|
2012-04-28 00:42:03 +00:00
|
|
|
|
|
|
|
@classmethod
|
|
|
|
def expr(klass):
|
|
|
|
e = pp.Literal("<").suppress()
|
2012-04-28 05:12:39 +00:00
|
|
|
e = e + v_naked_literal
|
2012-04-28 00:42:03 +00:00
|
|
|
return e.setParseAction(lambda x: klass(*x))
|
|
|
|
|
2012-10-30 20:32:21 +00:00
|
|
|
def freeze(self, settings):
|
|
|
|
return self
|
|
|
|
|
2012-04-28 00:42:03 +00:00
|
|
|
def get_generator(self, settings):
|
2015-04-22 03:49:17 +00:00
|
|
|
if not settings.staticdir:
|
2015-05-02 04:17:00 +00:00
|
|
|
raise exceptions.FileAccessDenied("File access disabled.")
|
2012-07-22 11:46:56 +00:00
|
|
|
s = os.path.expanduser(self.path)
|
2015-04-22 03:49:17 +00:00
|
|
|
s = os.path.normpath(
|
|
|
|
os.path.abspath(os.path.join(settings.staticdir, s))
|
|
|
|
)
|
|
|
|
uf = settings.unconstrained_file_access
|
|
|
|
if not uf and not s.startswith(settings.staticdir):
|
2015-05-02 04:17:00 +00:00
|
|
|
raise exceptions.FileAccessDenied(
|
2014-10-25 01:24:05 +00:00
|
|
|
"File access outside of configured directory"
|
|
|
|
)
|
2012-07-22 11:46:56 +00:00
|
|
|
if not os.path.isfile(s):
|
2015-05-02 04:17:00 +00:00
|
|
|
raise exceptions.FileAccessDenied("File not readable")
|
|
|
|
return generators.FileGenerator(s)
|
2012-04-28 00:42:03 +00:00
|
|
|
|
2012-10-23 22:32:53 +00:00
|
|
|
def spec(self):
|
2014-10-26 03:27:25 +00:00
|
|
|
return "<'%s'"%self.path.encode("string_escape")
|
2012-04-28 00:42:03 +00:00
|
|
|
|
|
|
|
|
2015-05-03 00:53:28 +00:00
|
|
|
TokValue = pp.MatchFirst(
|
2012-04-28 00:42:03 +00:00
|
|
|
[
|
2015-05-03 00:53:28 +00:00
|
|
|
TokValueGenerate.expr(),
|
|
|
|
TokValueFile.expr(),
|
|
|
|
TokValueLiteral.expr()
|
2012-04-28 00:42:03 +00:00
|
|
|
]
|
|
|
|
)
|
|
|
|
|
|
|
|
|
2015-05-03 00:53:28 +00:00
|
|
|
TokNakedValue = pp.MatchFirst(
|
2012-06-28 22:42:15 +00:00
|
|
|
[
|
2015-05-03 00:53:28 +00:00
|
|
|
TokValueGenerate.expr(),
|
|
|
|
TokValueFile.expr(),
|
|
|
|
TokValueLiteral.expr(),
|
|
|
|
TokValueNakedLiteral.expr(),
|
2012-06-28 22:42:15 +00:00
|
|
|
]
|
|
|
|
)
|
|
|
|
|
|
|
|
|
2015-05-03 00:53:28 +00:00
|
|
|
TokOffset = pp.MatchFirst(
|
2014-10-25 01:24:05 +00:00
|
|
|
[
|
|
|
|
v_integer,
|
|
|
|
pp.Literal("r"),
|
|
|
|
pp.Literal("a")
|
|
|
|
]
|
|
|
|
)
|
2012-07-23 05:19:25 +00:00
|
|
|
|
|
|
|
|
2015-05-02 20:02:13 +00:00
|
|
|
class _Component(Token):
|
2012-10-28 21:00:41 +00:00
|
|
|
"""
|
2015-05-02 20:02:13 +00:00
|
|
|
A value component of the primary specification of an message.
|
2015-05-02 20:51:57 +00:00
|
|
|
Components produce byte values desribe the bytes of the message.
|
2012-10-28 21:00:41 +00:00
|
|
|
"""
|
2012-10-28 01:15:29 +00:00
|
|
|
@abc.abstractmethod
|
2012-10-28 21:00:41 +00:00
|
|
|
def values(self, settings): # pragma: no cover
|
2012-10-28 04:39:58 +00:00
|
|
|
"""
|
2015-05-02 20:51:57 +00:00
|
|
|
A sequence of values, which can either be strings or generators.
|
2012-10-28 04:39:58 +00:00
|
|
|
"""
|
2015-05-02 20:51:57 +00:00
|
|
|
pass
|
2012-10-28 01:15:29 +00:00
|
|
|
|
|
|
|
def string(self, settings=None):
|
2012-10-28 04:39:58 +00:00
|
|
|
"""
|
2012-10-28 09:00:19 +00:00
|
|
|
A string representation of the object.
|
2012-10-28 04:39:58 +00:00
|
|
|
"""
|
2012-10-28 01:15:29 +00:00
|
|
|
return "".join(i[:] for i in self.values(settings or {}))
|
|
|
|
|
|
|
|
|
2015-05-02 10:32:57 +00:00
|
|
|
class KeyValue(_Component):
|
|
|
|
"""
|
|
|
|
A key/value pair.
|
|
|
|
klass.preamble: leader
|
|
|
|
"""
|
2012-10-27 01:00:50 +00:00
|
|
|
def __init__(self, key, value):
|
|
|
|
self.key, self.value = key, value
|
|
|
|
|
|
|
|
@classmethod
|
|
|
|
def expr(klass):
|
2015-05-02 10:32:57 +00:00
|
|
|
e = pp.Literal(klass.preamble).suppress()
|
2015-05-03 00:53:28 +00:00
|
|
|
e += TokValue
|
2012-10-27 01:00:50 +00:00
|
|
|
e += pp.Literal("=").suppress()
|
2015-05-03 00:53:28 +00:00
|
|
|
e += TokValue
|
2012-10-27 01:00:50 +00:00
|
|
|
return e.setParseAction(lambda x: klass(*x))
|
|
|
|
|
2012-10-29 03:31:35 +00:00
|
|
|
def spec(self):
|
2015-05-02 10:32:57 +00:00
|
|
|
return "%s%s=%s"%(self.preamble, self.key.spec(), self.value.spec())
|
2012-10-29 03:31:35 +00:00
|
|
|
|
2012-10-30 20:32:21 +00:00
|
|
|
def freeze(self, settings):
|
2015-05-02 10:32:57 +00:00
|
|
|
return self.__class__(
|
|
|
|
self.key.freeze(settings), self.value.freeze(settings)
|
2014-10-25 01:24:05 +00:00
|
|
|
)
|
2012-11-15 22:31:04 +00:00
|
|
|
|
|
|
|
|
2015-05-02 09:27:11 +00:00
|
|
|
class CaselessLiteral(_Component):
|
|
|
|
"""
|
|
|
|
A caseless token that can take only one value.
|
|
|
|
"""
|
2015-04-22 03:49:17 +00:00
|
|
|
def __init__(self, value):
|
|
|
|
self.value = value
|
|
|
|
|
|
|
|
@classmethod
|
|
|
|
def expr(klass):
|
2015-04-28 22:02:16 +00:00
|
|
|
spec = pp.CaselessLiteral(klass.TOK)
|
2015-04-22 03:49:17 +00:00
|
|
|
spec = spec.setParseAction(lambda x: klass(*x))
|
|
|
|
return spec
|
|
|
|
|
|
|
|
def values(self, settings):
|
2015-04-28 22:02:16 +00:00
|
|
|
return self.TOK
|
2015-04-22 03:49:17 +00:00
|
|
|
|
|
|
|
def spec(self):
|
2015-04-28 22:02:16 +00:00
|
|
|
return self.TOK
|
2015-04-22 03:49:17 +00:00
|
|
|
|
|
|
|
def freeze(self, settings):
|
|
|
|
return self
|
|
|
|
|
|
|
|
|
2015-05-02 09:27:11 +00:00
|
|
|
class OptionsOrValue(_Component):
|
|
|
|
"""
|
|
|
|
Can be any of a specified set of options, or a value specifier.
|
|
|
|
"""
|
2015-05-02 10:32:57 +00:00
|
|
|
preamble = ""
|
2015-05-03 01:54:52 +00:00
|
|
|
options = []
|
2012-06-24 05:23:37 +00:00
|
|
|
def __init__(self, value):
|
2015-05-03 00:59:21 +00:00
|
|
|
# If it's a string, we were passed one of the options, so we lower-case
|
2012-06-24 05:23:37 +00:00
|
|
|
# it to be canonical. The user can specify a different case by using a
|
|
|
|
# string value literal.
|
2015-05-02 10:32:57 +00:00
|
|
|
self.option_used = False
|
2012-06-24 05:23:37 +00:00
|
|
|
if isinstance(value, basestring):
|
2015-05-03 01:54:52 +00:00
|
|
|
for i in self.options:
|
|
|
|
# Find the exact option value in a case-insensitive way
|
|
|
|
if i.lower() == value.lower():
|
|
|
|
self.option_used = True
|
|
|
|
value = TokValueLiteral(i)
|
|
|
|
break
|
2012-06-24 05:23:37 +00:00
|
|
|
self.value = value
|
|
|
|
|
|
|
|
@classmethod
|
|
|
|
def expr(klass):
|
2015-05-02 09:27:11 +00:00
|
|
|
parts = [pp.CaselessLiteral(i) for i in klass.options]
|
2012-06-24 05:23:37 +00:00
|
|
|
m = pp.MatchFirst(parts)
|
2015-05-03 00:53:28 +00:00
|
|
|
spec = m | TokValue.copy()
|
2012-06-24 05:23:37 +00:00
|
|
|
spec = spec.setParseAction(lambda x: klass(*x))
|
2015-05-02 10:32:57 +00:00
|
|
|
if klass.preamble:
|
|
|
|
spec = pp.Literal(klass.preamble).suppress() + spec
|
2012-06-24 05:23:37 +00:00
|
|
|
return spec
|
|
|
|
|
2012-10-28 01:15:29 +00:00
|
|
|
def values(self, settings):
|
|
|
|
return [
|
|
|
|
self.value.get_generator(settings)
|
|
|
|
]
|
|
|
|
|
2012-10-29 03:31:35 +00:00
|
|
|
def spec(self):
|
|
|
|
s = self.value.spec()
|
2015-05-02 09:27:11 +00:00
|
|
|
if s[1:-1].lower() in self.options:
|
2012-10-29 03:31:35 +00:00
|
|
|
s = s[1:-1].lower()
|
2015-05-02 10:32:57 +00:00
|
|
|
return "%s%s"%(self.preamble, s)
|
2012-10-29 03:31:35 +00:00
|
|
|
|
2012-10-30 20:32:21 +00:00
|
|
|
def freeze(self, settings):
|
2015-05-02 09:27:11 +00:00
|
|
|
return self.__class__(self.value.freeze(settings))
|
2012-10-30 20:32:21 +00:00
|
|
|
|
2012-06-24 05:23:37 +00:00
|
|
|
|
2015-05-02 09:42:09 +00:00
|
|
|
class Integer(_Component):
|
|
|
|
def __init__(self, value):
|
|
|
|
self.value = str(value)
|
2012-10-28 21:00:41 +00:00
|
|
|
|
|
|
|
@classmethod
|
|
|
|
def expr(klass):
|
|
|
|
e = v_integer.copy()
|
|
|
|
return e.setParseAction(lambda x: klass(*x))
|
|
|
|
|
|
|
|
def values(self, settings):
|
2015-05-02 20:51:57 +00:00
|
|
|
return self.value
|
2012-10-28 21:00:41 +00:00
|
|
|
|
2012-10-29 03:31:35 +00:00
|
|
|
def spec(self):
|
2015-05-02 09:42:09 +00:00
|
|
|
return "%s"%(self.value)
|
2012-10-29 03:31:35 +00:00
|
|
|
|
2012-10-30 20:32:21 +00:00
|
|
|
def freeze(self, settings):
|
2015-05-02 09:27:11 +00:00
|
|
|
return self
|
2012-10-30 20:32:21 +00:00
|
|
|
|
2012-10-28 21:00:41 +00:00
|
|
|
|
2015-05-03 00:54:25 +00:00
|
|
|
class Value(_Component):
|
2015-05-02 09:42:09 +00:00
|
|
|
"""
|
2015-05-03 00:53:28 +00:00
|
|
|
A value component lead by an optional preamble.
|
2015-05-02 09:42:09 +00:00
|
|
|
"""
|
2015-05-03 00:53:28 +00:00
|
|
|
preamble = ""
|
|
|
|
|
2012-10-28 21:00:41 +00:00
|
|
|
def __init__(self, value):
|
|
|
|
self.value = value
|
|
|
|
|
|
|
|
@classmethod
|
|
|
|
def expr(klass):
|
2015-05-03 00:53:28 +00:00
|
|
|
e = (TokValue | TokNakedValue)
|
|
|
|
if klass.preamble:
|
|
|
|
e = pp.Literal(klass.preamble).suppress() + e
|
2012-10-28 21:00:41 +00:00
|
|
|
return e.setParseAction(lambda x: klass(*x))
|
|
|
|
|
|
|
|
def values(self, settings):
|
|
|
|
return [self.value.get_generator(settings)]
|
|
|
|
|
2012-10-29 03:31:35 +00:00
|
|
|
def spec(self):
|
2015-05-02 09:42:09 +00:00
|
|
|
return "%s%s"%(self.preamble, self.value.spec())
|
2012-10-29 03:31:35 +00:00
|
|
|
|
2012-10-30 20:32:21 +00:00
|
|
|
def freeze(self, settings):
|
2015-05-02 09:42:09 +00:00
|
|
|
return self.__class__(self.value.freeze(settings))
|
2015-05-03 01:54:52 +00:00
|
|
|
|
|
|
|
|
|
|
|
class IntField(_Component):
|
|
|
|
"""
|
|
|
|
An integer field, where values can optionally specified by name.
|
|
|
|
"""
|
|
|
|
names = {}
|
|
|
|
max = 16
|
|
|
|
preamble = ""
|
|
|
|
|
|
|
|
def __init__(self, value):
|
|
|
|
self.origvalue = value
|
|
|
|
self.value = self.names.get(value, value)
|
|
|
|
if self.value > self.max:
|
|
|
|
raise exceptions.ParseException(
|
|
|
|
"Value can't exceed %s"%self.max, 0, 0
|
|
|
|
)
|
|
|
|
|
|
|
|
@classmethod
|
|
|
|
def expr(klass):
|
|
|
|
parts = [pp.CaselessLiteral(i) for i in klass.names.keys()]
|
|
|
|
m = pp.MatchFirst(parts)
|
|
|
|
spec = m | v_integer.copy()
|
|
|
|
spec = spec.setParseAction(lambda x: klass(*x))
|
|
|
|
if klass.preamble:
|
|
|
|
spec = pp.Literal(klass.preamble).suppress() + spec
|
|
|
|
return spec
|
|
|
|
|
|
|
|
def values(self, settings):
|
|
|
|
return [str(self.value)]
|
|
|
|
|
|
|
|
def spec(self):
|
|
|
|
return "%s%s"%(self.preamble, self.origvalue)
|