import threading
from datetime import datetime
import dateutil.parser
import os, sys
from redbeans.formats import Format
from redbeans.html import HTMLFormat
from redbeans.latex import LaTeXFormat
from redbeans.creole import tokenize
from redbeans.tokens import *
from . import translators
# Add to this to give additional wiki-based output formats
FORMATS = {'txt': Format,
'html': HTMLFormat,
'tex': LaTeXFormat}
class flavor(object):
# Indexed propvals are OK to search by and are loaded synchonously;
# non-indexed propvals are loaded asychronously when editing.
indexed = True
binary = False
# A raw propval never gets its contents treated as wikitext.
raw = False
# A quick propval can be (top-level) evaluate()d in constant time.
quick = False
is_ref = False
owned = False
default = ""
default_formats = (u'.html',)
@staticmethod
def stringify(p):
return unicode(p)
@staticmethod
def getExtensions(propval):
return FORMATS.keys()
@classmethod
def evaluate(cls, wikitext, parser, propval=None):
"""This is called only for top-level evaluations."""
toks = cls.tokenize(wikitext, parser, propval)
if parser.format.text_based:
return parser.render(toks)
else:
return list(parser.irender(toks))
@classmethod
def tokenize(cls, wikitext, parser, propval=None):
yield Text(cls.stringify(cls.toPython(
wikitext, parser, propval=propval)))
@classmethod
def toPython(cls, wikitext, parser, propval=None):
import wiki
raise wiki.WikiException("Flavor %s has no python conversion, so you can't use prop '%s' in an expression!"%(cls.__name__,propval.propname if propval is not None else wikitext))
class text(flavor):
"""Freeform or longer text; not searchable."""
indexed = False
big = True
@staticmethod
def tokenize(wikitext, parser, propval=None):
#print "Evaluating test: %s" % repr(wikitext)
return tokenize(wikitext, parser.error_func)
@staticmethod
def toPython(wikitext, parser, propval=None):
return parser.parse(wikitext, format=Format())
class raw(flavor):
"""Short searchable text that's not evaluated."""
raw = True
quick = True
@staticmethod
def toPython(wikitext, parser, propval=None):
return wikitext
@staticmethod
def tokenize(wikitext, parser, propval=None):
yield Text(raw.toPython(
wikitext, parser, propval=propval))
class macro(text):
"""Macro text only evaluated in the context of another propval."""
indexed = False
big = True
quick = True
@staticmethod
def evaluate(wikitext, parser, propval=None):
if propval is not None:
import wiki, formatting
nv, deps = formatting.nice_value(propval)
wiki.addDeps(deps)
return parser.render([Start(CODEBLOCK), Text(nv),
End(CODEBLOCK)])
else:
return text.evaluate(self, wikitext, parser, propval)
class blob(flavor):
"""Binary objects with no markup."""
indexed = False
binary = True
default_formats = (u'.png',u'.svg',u'.jpg')
image_formats = (u'.png',u'.jpg',u'.gif',u'.svg',u'.eps')
@staticmethod
def getExtensions(propval):
# TODO(xavid): This is a horrible hack.
while propval.value == '<>':
propval = propval.element.get_parent().get_propval(
propval.propname)
ext = propval.format
if ext == 'creole' and not propval.value:
# We just have empty.png right now.
return ['.png']+FORMATS.keys()
else:
assert ext != 'creole', propval
return [ext]+FORMATS.keys()
@staticmethod
def evaluate(wikitext, parser, propval=None):
# TODO(xavid): This is a horrible hack.
assert isinstance(wikitext, str), wikitext
if wikitext == '<>':
assert propval is not None and str(propval.value) == '<>', repr(str(propval.value))
while propval.value == '<>':
propval = propval.element.get_parent().get_propval(
propval.propname)
wikitext = propval.value
if propval is not None:
ef = propval.format
else:
ef = '.dat'
href = '%s.%s%s' % (propval.element.ename, propval.propname, ef)
if wikitext is '' and parser == '.png':
# Empty image.
with open(os.path.join(os.path.dirname(__file__), 'data',
'empty.png')) as fil:
return fil.read()
elif ef == parser or (ef in FORMATS and
hasattr(parser,'get_format')
and isinstance(parser.get_format(),FORMATS[ef])):
return wikitext
elif ef in blob.image_formats:
return parser.render([
Start(LINK, href),
Entity(IMAGE, '%s.%s' % (propval.element.ename,
propval.propname)),
End(LINK, href)])
elif ef == '.txt':
return parser.render([
Text(unicode(wikitext,'utf-8'))])
else:
return parser.render([
Start(LINK, href),
Text(u"[%s data]" % (ef)),
End(LINK, href)])
@staticmethod
def tokenize(wikitext, parser, propval=None):
assert False
class string(flavor):
"""Short searchable text."""
@staticmethod
def tokenize(wikitext, parser, propval=None):
return tokenize(wikitext, parser.error_func)
@staticmethod
def toPython(wikitext,parser,propval=None):
assert isinstance(wikitext, unicode)
return parser.parse(wikitext, format=Format())
def str_to_bool(s):
return s.strip()[0].lower() not in ('n','f','0')
class boolean(flavor):
"""True or false."""
default = False
@staticmethod
def toPython(wikitext,parser,propval=None):
text = parser.parse(wikitext, format=Format())
return str_to_bool(text)
def restricted(conv, defa, help, stringify=None):
class rflav(flavor):
__doc__ = help
default = defa
@staticmethod
def toPython(wikitext,parser,propval=None):
text = parser.parse(wikitext, format=Format())
try:
show = conv(text)
except ValueError:
show = defa
return show
if stringify is not None:
rflav.stringify = staticmethod(stringify)
return rflav
integer = restricted(int, -1, """An integral number.""")
TIMESTAMP_FORMAT = "%Y-%m-%d %H:%M:%S"
# TODO(xavid): This would probably be cleaner with restricted as a superclass.
# Probably everything should stop being static and integer should
# be an instance of class restricted.
# TODO(xavid): Handle invalid dates as N/A, not as long ago.
# We could use a more human-readable format for stringify, but it can't
# be pretty.date() unless we make it add a dependency on DISCORDIA.
timestamp = restricted(lambda w: dateutil.parser.parse(w),
None, """A date and time.""",
lambda p: (unicode(p.strftime(TIMESTAMP_FORMAT))
if p is not None else u""))
timestamp.now = staticmethod(lambda: datetime.strftime(
datetime.now(), TIMESTAMP_FORMAT))
class Reference(object):
def __init__(self, element, args, owner):
assert hasattr(element, 'ename'), element
self.element = element
self.args = args
self.owner = owner
assert owner is not None, (element, args, owner)
for a in args:
assert isinstance(a, unicode), args
def has_propval(self, propname):
return self.element.has_propval(propname)
def __repr__(self):
return "[Reference: %s/%s]" % (self.element.ename, '/'.join(self.args))
def __eq__(self, other):
return (self.element == getattr(other, 'element', None)
and self.args == getattr(other, 'args', None))
def maybe_ref(d):
from . import structure
element = structure.get_element(d['ename'])
if element is None:
from . import wiki
raise wiki.WikiException('Undefined element %s!' % d['ename'])
elif d['args'] is not None or d['owner'] is not None:
return Reference(element, d['args'] or [], d['owner'])
else:
return element
class references(flavor):
"""A list of links to other elements."""
default = []
owned = True
@staticmethod
def _enames(wikitext, propval=None):
import wiki
dicts, errors = wiki.get_reference_enames(wikitext, propval=propval)
#print >>sys.stderr, "refs=", refs
return dicts, errors
@staticmethod
def toPython(wikitext,parser,propval=None):
return [maybe_ref(d)
for d in references._enames(wikitext, propval)[0]
if d is not None]
@staticmethod
def tokenize(wikitext, parser, propval=None):
dicts, errors = references._enames(wikitext, propval)
for d in dicts:
ename = d['ename']
args = d['args']
yield Start(UNORDERED_ITEM, 1)
if args:
yield Entity(LINK, ename + '/' + '/'.join(args))
else:
yield Entity(LINK, ename)
yield End(UNORDERED_ITEM, 1)
for e in errors:
yield Start(ERROR)
for t in tokenize(e):
yield t
yield End(ERROR)
class reference(flavor):
"""A single link to another element."""
default = None
owned = True
is_ref = True
@staticmethod
def _ename(wikitext, propval=None):
import wiki
dicts, errors = wiki.get_reference_enames(wikitext, propval=propval)
if len(dicts) > 1:
raise wiki.WikiException("Expected 1 reference, got %d!"
% (len(refs)))
if len(dicts) == 0:
return None, errors
else:
return dicts.pop(), errors
@staticmethod
def toPython(wikitext,parser,propval=None):
d, errors = reference._ename(wikitext, propval)
if d is None:
return None
return maybe_ref(d)
MARKUP = u'[[%s]]'
@staticmethod
def element_to_string(element):
from . import wiki
val, deps, metadata = wiki.evaluate(reference.MARKUP % element.ename,
'txt', element=element,
flavor=reference)
return val
# TODO(xavid): share code with references better
@staticmethod
def tokenize(wikitext, parser, propval=None):
d, errors = reference._ename(wikitext, propval)
if d is not None:
ename = d['ename']
args = d['args']
owner = d['owner']
from . import wiki
eover_dict = {}
if owner is not None:
eover_dict[u'owner'] = owner
with wiki.eoverride(eover_dict):
if args:
yield Entity(LINK, ename + '/' + '/'.join(args))
else:
yield Entity(LINK, ename)
for e in errors:
yield Start(ERROR)
for t in tokenize(e):
yield t
yield End(ERROR)
class labeledrefs(flavor):
"""A list of links to other elements with label text."""
default = []
owned = True
@staticmethod
def _ename_label_pairs(wikitext, propval=None):
import wiki
dictlabels, errors = wiki.get_reference_enames_with_labels(
wikitext, propval=propval)
return dictlabels, errors
@staticmethod
def toPython(wikitext,parser,propval=None):
from . import structure
ret = []
def make_render_label(l):
def render_label(argstr, content):
return l
return render_label
for e,l in ((structure.get_element(d['ename']), l)
for d,l in
labeledrefs._ename_label_pairs(wikitext, propval)[0]):
if e is not None:
ret.append([e, make_render_label(l)])
return ret
@staticmethod
def tokenize(wikitext, parser, propval=None):
elps, errors = labeledrefs._ename_label_pairs(wikitext, propval)
for d,label in elps:
yield Start(UNORDERED_ITEM, 1)
yield Entity(LINK, d['ename'])
yield Text(u": ")
if label is not None:
for t in label:
yield t
else:
yield Error("No label!")
yield End(UNORDERED_ITEM, 1)
for e in errors:
yield Start(ERROR)
for t in tokenize(e):
yield t
yield End(ERROR)
FLAVORS = {
'text': text,
'string': string,
'integer': integer,
'boolean': boolean,
'references': references,
'reference': reference,
'labeledrefs': labeledrefs,
'blob': blob,
'macro': macro,
'raw': raw,
'timestamp': timestamp,
}