323 lines
7.7 KiB
Python
Executable File
323 lines
7.7 KiB
Python
Executable File
#!/usr/bin/env python3
|
|
#******************************************************************************
|
|
# Copyright (C) 2018 Thomas "Cakeisalie5" Touhey <thomas@touhey.fr>
|
|
# This file is part of the textoutpc project, which is MIT-licensed.
|
|
#******************************************************************************
|
|
""" Base classes to use with options (tags, smileys) in textoutpc, with a
|
|
manager class.
|
|
|
|
For your tag to be used as a textoutpc tag, you have to make it
|
|
inherit one of the `TextoutBlockTag` or `TextoutInlineTag` classes.
|
|
|
|
Making separate tag modules is possible through the manager class,
|
|
which allows not to hardcode the tags into the module. """
|
|
|
|
from functools import partial as _p
|
|
from inspect import ismodule as _ismod, isclass as _isclass, \
|
|
getargspec as _getargspec, getfullargspec as _getfullargspec, \
|
|
currentframe as _currentframe, getouterframes as _getouterframes
|
|
from importlib import import_module as _importmod
|
|
|
|
from ._html import SmileyConvertor as _htmlsm
|
|
|
|
__all__ = ["TextoutOptions",
|
|
"TextoutTag", "TextoutBlockTag", "TextoutInlineTag", "TextoutParagraphTag",
|
|
"TextoutSmiley", "TextoutVideo"]
|
|
|
|
def _getargscount(func):
|
|
try:
|
|
return len(_getfullargspec(func).args)
|
|
except:
|
|
return len(_getargspec(func).args)
|
|
|
|
# ---
|
|
# Tags.
|
|
# ---
|
|
|
|
# Main base tag class.
|
|
# For more about defining a tag, see `doc/tags.md`.
|
|
|
|
class TextoutTag:
|
|
""" The textout tag base class.
|
|
Is initialized with these values:
|
|
|
|
<name><content><name>
|
|
| name: "<name>" (only special chars such as `)
|
|
| value: None
|
|
[<name>]<content>[/<name>]
|
|
| name: "[<name>]"
|
|
| value: None
|
|
[<name>]<content>[/] (when possible)
|
|
| name: "[<name>]"
|
|
| value: None
|
|
[<name>=<value>]<content>[/<name>]
|
|
| name: "[<name>]"
|
|
| value: "<value>"
|
|
[<name>=<value>]<content>[/] (when possible)
|
|
| name: "[<name>]"
|
|
| value: "<value>" """
|
|
|
|
aliases = ()
|
|
|
|
def __init__(self, name, value, ot, tweaks, options):
|
|
""" Initialize the textout tag with the documented members. """
|
|
|
|
# Store internal data.
|
|
|
|
self.__name = name
|
|
self.__value = value
|
|
self.__output_type = ot
|
|
self.__tweaks = tweaks
|
|
self.__options = options
|
|
|
|
self.output_type = ot
|
|
|
|
# Call both prepare functions.
|
|
|
|
if hasattr(self, 'prepare'):
|
|
try:
|
|
assert _getargscount(self.prepare) == 4
|
|
args = (name, value, ot)
|
|
except:
|
|
args = (name, value)
|
|
self.prepare(*args)
|
|
if hasattr(self, 'prepare_' + ot):
|
|
prep = getattr(self, 'prepare_' + ot)
|
|
try:
|
|
assert len(_getargspec(prep).args) == 4
|
|
args = (name, value, ot)
|
|
except:
|
|
args = (name, value)
|
|
prep(*args)
|
|
|
|
# Prepare the preprocessing elements.
|
|
|
|
if hasattr(self, 'preprocess'):
|
|
if hasattr(self, 'preprocess_' + ot):
|
|
self.__preprocess0 = self.preprocess
|
|
self.preprocess = self.__preprocess_double
|
|
elif hasattr(self, 'preprocess_' + ot):
|
|
self.preprocess = getattr(self, 'preprocess_' + ot)
|
|
|
|
if hasattr(self, 'preprocess'):
|
|
self.__preprocess2 = self.preprocess
|
|
self.preprocess = self.__preprocess_and_prepare
|
|
else:
|
|
self.__after_preprocess()
|
|
|
|
if hasattr(self, 'default_' + ot):
|
|
self.default = getattr(self, 'default_' + ot)
|
|
|
|
def __repr__(self):
|
|
return f"{self.__class__.__name__}(name = {repr(self.__name)}, " \
|
|
f"value = {repr(self.__value)}, " \
|
|
f"ot = {repr(self.__output_type)})"
|
|
|
|
def __preprocess_double(self, content):
|
|
""" Preprocess using the two methods. """
|
|
|
|
ct = self.__preprocess0(content)
|
|
if ct != None: content = ct; del ct
|
|
ct = self.__preprocess1(content)
|
|
if ct != None: content = ct; del ct
|
|
return content
|
|
|
|
def __preprocess_and_prepare(self, content):
|
|
""" Preprocess and do the things after. """
|
|
|
|
ret = self.__preprocess2(content)
|
|
self.__after_preprocess()
|
|
return ret
|
|
|
|
def __out(self, name):
|
|
""" Generic function to call two output functions of the same
|
|
type. """
|
|
|
|
getattr(self, '__' + name)()
|
|
getattr(self, name + '_' + self.__output_type)()
|
|
|
|
def __after_preprocess(self):
|
|
""" After preprocessing, check the begin, content and end that may
|
|
have been set by the preprocessing function. """
|
|
|
|
ot = self.__output_type
|
|
|
|
for otype in ('begin', 'content', 'end'):
|
|
if hasattr(self, otype):
|
|
if hasattr(self, otype + '_' + ot):
|
|
setattr(self, '__' + otype, getattr(self, otype))
|
|
setattr(self, otype, _p(self.__out, otype))
|
|
elif hasattr(self, otype + '_' + ot):
|
|
setattr(self, otype, getattr(self, otype + '_' + ot))
|
|
|
|
def tweak(self, key, default = None):
|
|
try:
|
|
return self.__tweaks[key]
|
|
except KeyError:
|
|
return default
|
|
|
|
def video(self, *args, **kwargs):
|
|
return self.__options.get_video(*args, **kwargs)
|
|
|
|
# Role-specific base tag classes.
|
|
|
|
class TextoutBlockTag(TextoutTag):
|
|
pass
|
|
class TextoutInlineTag(TextoutTag):
|
|
pass
|
|
|
|
# Default tag: paragraph.
|
|
|
|
class TextoutParagraphTag(TextoutBlockTag):
|
|
""" Main tag for basic paragraphs. """
|
|
|
|
notempty = True
|
|
|
|
def begin_html(self):
|
|
return '<p>'
|
|
|
|
def end_html(self):
|
|
return '</p>'
|
|
|
|
# ---
|
|
# Smileys.
|
|
# ---
|
|
|
|
class TextoutSmiley:
|
|
""" Base class for smileys. """
|
|
|
|
aliases = ()
|
|
url = None
|
|
|
|
def __repr__(self):
|
|
return f"{self.__class__.__name__}(aliases = {repr(self.aliases)}, " \
|
|
f"url = {repr(self.url)})"
|
|
|
|
# ---
|
|
# Videos.
|
|
# ---
|
|
|
|
class TextoutVideo:
|
|
""" Base class for videos. """
|
|
|
|
def __init__(self, url):
|
|
raise ValueError("no URL supported")
|
|
|
|
def embed(self):
|
|
return "http://example.org/"
|
|
|
|
# ---
|
|
# Options extractor and manager.
|
|
# ---
|
|
|
|
_builtin_module = None
|
|
def _get_builtin_module():
|
|
""" Get the `.builtin` module. """
|
|
|
|
global _builtin_module
|
|
|
|
if _builtin_module == None:
|
|
_builtin_module = _importmod('..builtin', __name__)
|
|
return _builtin_module
|
|
|
|
class TextoutOptions:
|
|
""" Options manager.
|
|
Object responsible for getting the tags. """
|
|
|
|
def __init__(self, *modules, default = True):
|
|
self._aliases = {}
|
|
self._s_aliases = {}
|
|
self._videos = []
|
|
|
|
if default:
|
|
self.add(_get_builtin_module())
|
|
for mod in modules:
|
|
self.add(mod)
|
|
|
|
def add(self, element):
|
|
""" Add an option. """
|
|
|
|
if isinstance(element, str):
|
|
element = str(element)
|
|
element = _importmod(element,
|
|
_getouterframes(_currentframe(), 1)[0].name)
|
|
|
|
if _ismod(element):
|
|
self.__extract(element)
|
|
return True
|
|
|
|
if _isclass(element) and issubclass(element, TextoutTag):
|
|
for alias in element.aliases:
|
|
self._aliases[alias] = element
|
|
return True
|
|
|
|
if _isclass(element) and issubclass(element, TextoutSmiley):
|
|
for alias in element.aliases:
|
|
self._s_aliases[alias] = element
|
|
|
|
self._htmlsm = None
|
|
return True
|
|
|
|
if _isclass(element) and issubclass(element, TextoutVideo):
|
|
if not any(video is element for video in self._videos):
|
|
self._videos.append(element)
|
|
|
|
def __extract(self, module):
|
|
""" Extract options from a module. """
|
|
|
|
tags = []
|
|
smileys = []
|
|
|
|
# Obtain the list of properties from the module.
|
|
|
|
try:
|
|
ds = module.__all__
|
|
except:
|
|
ds = dir(module)
|
|
|
|
# Get the submodules from the module (usually different files in the
|
|
# tags module folder).
|
|
|
|
for submodule in (obj for name, obj in ((nm, getattr(module, nm)) \
|
|
for nm in ds) if (name == '__init__' or name[0] != '_') \
|
|
and _ismod(obj)):
|
|
self.__extract(submodule)
|
|
|
|
# Extract the tags from the current module.
|
|
|
|
for obj in (obj for name, obj in ((nm, getattr(module, nm)) \
|
|
for nm in ds) if name[0] != '_'):
|
|
self.add(obj)
|
|
|
|
def get_smileys(self):
|
|
""" Get the smileys dictionary. """
|
|
|
|
return self._s_aliases.copy()
|
|
|
|
def htmlsmileys(self, text):
|
|
""" Get the smileys convertor for HTML. """
|
|
|
|
if not self._htmlsm:
|
|
self._htmlsm = _htmlsm(self._s_aliases)
|
|
return self._htmlsm.convert(text)
|
|
|
|
def get_video(self, url):
|
|
""" Get a video using its URL. """
|
|
|
|
for video in self._videos:
|
|
try:
|
|
v = video(url)
|
|
except:
|
|
continue
|
|
break
|
|
else:
|
|
raise ValueError("invalid video URL")
|
|
return v
|
|
|
|
def get_tag(self, name):
|
|
""" Get the tag class corresponding to a name. """
|
|
|
|
return self._aliases[name]
|
|
|
|
# End of file.
|