# -*- coding: utf-8 -*-
from collections import Sequence
from ruamel.yaml.comments import CommentedMap
from freckles.defaults import freckles_src_template_dir
from freckles.frecklecutable import FrecklecutableMixin
from freckles.frecklet.doc import render_html, render_markdown
from freckles.utils.utils import (
generate_frecklet_src_jinja_env,
convert_dataclass_type_filter,
)
from frutils import dict_merge, readable_yaml
from frutils.jinja2_filters import camelize_filter
from ting.ting_attributes import MultiCacheResult, Arg
from .tasks import * # noqa
from ting.attributes.rendering import JinjaTemplateMixin # noqa
log = logging.getLogger("freckles")
[docs]def get_src_template(template_name):
jinja_env = generate_frecklet_src_jinja_env(
template_dir=freckles_src_template_dir,
extra_filters={"get_dataclass_type": convert_dataclass_type_filter},
)
if template_name is None:
template_name = "python_3_src.j2"
template = jinja_env.get_template(name=template_name)
return template
[docs]class FreckletsTemplateKeysAttribute(TingAttribute):
[docs] def requires(self):
return ["_metadata"]
[docs] def provides(self):
return ["template_keys"]
[docs] def get_attribute(self, ting, attribute_name=None):
template_keys = get_template_keys(
ting._metadata[FRECKLETS_KEY], jinja_env=DEFAULT_FRECKLES_JINJA_ENV
)
return template_keys
[docs]class FreckletValidAttribute(TingAttribute):
def __init__(self):
pass
[docs] def provides(self):
return ["valid", "invalid_exception"]
[docs] def requires(self):
return ["exploded"]
[docs] def get_attribute(self, ting, attribute_name=None):
try:
exploded = ting.exploded
log.debug("checking exploded data structure: {}".format(exploded))
result = {"valid": True, "invalid_exception": None}
except (Exception) as e:
log.debug("Validation for frecklet failed.")
log.debug(e, exc_info=1)
result = {"valid": False, "invalid_exception": e}
return MultiCacheResult(**result)
[docs]class FreckletExplodedAttribute(TingAttribute):
def __init__(self):
pass
[docs] def provides(self):
return ["exploded"]
[docs] def requires(self):
return ["vars_frecklet", "doc", "tasklist", "const"]
[docs] def get_attribute(self, ting, attribute_name=None):
result = CommentedMap()
result["doc"] = ting.doc.exploded_dict()
result["const"] = ting.const
result["args"] = CommentedMap()
for k, arg in ting.vars_frecklet.items():
details = arg.pretty_print_dict(full_details=True)
result["args"][k] = details
result["frecklets"] = []
for task in ting.tasklist:
r = CommentedMap()
r[FRECKLET_KEY_NAME] = task[FRECKLET_KEY_NAME]
if TASK_KEY_NAME in task.keys() and task[TASK_KEY_NAME]:
r[TASK_KEY_NAME] = task[TASK_KEY_NAME]
if VARS_KEY in task.keys() and task[VARS_KEY]:
r[VARS_KEY] = task[VARS_KEY]
result["frecklets"].append(r)
return result
[docs]class FreckletConstAttribute(TingAttribute):
def __init__(self):
pass
[docs] def provides(self):
return [CONST_KEY_NAME]
[docs] def requires(self):
return ["_metadata"]
[docs] def get_attribute(self, ting, attribute_name=None):
result = ting._metadata.get(CONST_KEY_NAME, {})
return result
[docs]class FreckletConstArgsAttribute(TingAttribute):
def __init__(self):
pass
[docs] def provides(self):
return ["vars_{}".format(CONST_KEY_NAME)]
[docs] def requires(self):
return ["const", "args"]
[docs] def get_attribute(self, ting, attribute_name=None):
result = {}
for k, v in ting.const.items():
tks = get_template_keys(v, jinja_env=DEFAULT_FRECKLES_JINJA_ENV)
for tk in tks:
if tk in result.keys():
continue
is_auto_arg = False
if ting.args.get(tk, None) is None:
is_auto_arg = True
arg = Arg(
key=tk,
arg_dict=ting.args.get(tk, None),
default_schema=FRECKLES_DEFAULT_ARG_SCHEMA,
is_auto_arg=is_auto_arg,
)
result[tk] = arg
return result
[docs]class FreckletHtmlAttribute(TingAttribute):
def __init__(self):
pass
[docs] def provides(self):
return ["html"]
[docs] def requires(self):
return []
[docs] def get_attribute(self, ting, attribute_name=None):
try:
html = render_html(ting)
return html
except (Exception) as e:
return "<p>Can't render frecklet {}: {}".format(ting.id, e)
[docs]class FreckletMarkdownAttribute(TingAttribute):
def __init__(self):
pass
[docs] def provides(self):
return ["markdown"]
[docs] def requires(self):
return []
[docs] def get_attribute(self, ting, attribute_name=None):
try:
markdown = render_markdown(ting)
return markdown
except (Exception) as e:
import traceback
traceback.print_exc()
return "Can't render frecklet {}: {}".format(ting.id, e)
[docs]class PagelingMetadataAttribute(TingAttribute):
def __init__(self):
pass
[docs] def provides(self):
return ["pageling_metadata"]
[docs] def requires(self):
return ["wrapped"]
[docs] def get_attribute(self, ting, attribute_name=None):
frecklet = ting.wrapped
result = {}
result["title"] = frecklet.id
result["is_draft"] = frecklet.meta.get("draft", False)
return result
[docs]class PagelingContentAttribute(TingAttribute):
def __init__(self):
pass
[docs] def provides(self):
return ["pageling_content"]
[docs] def requires(self):
return ["wrapped"]
[docs] def get_attribute(self, ting, attribute_name=None):
frecklet = ting.wrapped
return str(frecklet.doc)
[docs]class PrettyPrintFrecklet(TingAttribute):
def __init__(self):
pass
[docs] def provides(self):
return ["pretty_print"]
[docs] def requires(self):
return ["_metadata"]
[docs] def get_attribute(self, ting, attribute_name=None):
result = CommentedMap()
for key in ["doc", "args", "meta", "frecklets"]:
if ting._metadata.get(key, None):
result[key] = ting._metadata[key]
return readable_yaml(result, ignore_aliases=True)
[docs]class FreckletClassNameAttribute(TingAttribute):
def __init__(self):
pass
[docs] def provides(self):
return ["class_name"]
[docs] def requires(self):
return ["id"]
[docs] def get_attribute(self, ting, attribute_name=None):
return camelize_filter(ting.id, replace_dashes=True)
[docs]class FreckletSrcAttribute(TingAttribute):
def __init__(self, template_name, target_attr=None):
self.package_name = "pycklets"
if target_attr is None:
target_attr = template_name
self.target_attr = target_attr
self.template = get_src_template(template_name=template_name)
[docs] def requires(self):
return ["render_template"]
[docs] def provides(self):
return [self.target_attr]
[docs] def get_attribute(self, ting, attribute_name=None):
module_name = ting.id.replace("-", "_")
src = ting.render_template(
ting_repl_name="frecklet",
template=self.template,
extra_repl_dict={
"package_name": self.package_name,
"module_name": module_name,
},
)
return src
[docs]class PagelingNavPathAttribute(TingAttribute):
def __init__(self):
pass
[docs] def provides(self):
return ["tree_path"]
[docs] def requires(self):
return ["wrapped"]
[docs] def get_attribute(self, ting, attribute_name=None):
frecklet = ting.wrapped
if hasattr(frecklet, "rel_path_no_ext"):
rel_path = frecklet.rel_path_no_ext
else:
rel_path = "misc/{}".format(frecklet.id)
tokens = rel_path.split(os.path.sep)
if len(tokens) == 1:
return "/" + tokens[0]
else:
return "/" + tokens[0] + "/" + tokens[-1]
FRECKLET_LOAD_CONFIG = {
"class_name": "Frecklet",
"attributes": [
{
"ArgsAttribute": {
"source_attr_name": "_metadata",
"target_attr_name": "args",
"index_attr_name": "_meta_parent_repo",
# "validate_list_attr": "template_keys",
}
},
{"DocAttribute": {"source_attr_name": "_metadata"}},
{"FreckletMetaAttribute": {"default": {}}},
"FreckletConstAttribute",
"FreckletConstArgsAttribute",
"FreckletClassNameAttribute",
"FreckletAugmentMetadataAttribute",
"FreckletsAttribute",
"TaskListDetailedAttribute",
"FreckletHtmlAttribute",
"FreckletMarkdownAttribute",
"FreckletExplodedAttribute",
"FreckletValidAttribute",
"VarsAttribute",
"PrettyPrintFrecklet",
"TaskTreeAttribute",
{
"FreckletSrcAttribute": {
"template_name": "python_3_src.j2",
"target_attr": "src_python_3",
}
},
{
"FreckletSrcAttribute": {
"template_name": "python_src.j2",
"target_attr": "src_python_2",
}
},
{
"VariablesAttribute": {
"target_attr_name": "vars_frecklet",
"default_argument_description": FRECKLES_DEFAULT_ARG_SCHEMA,
}
},
{
"VariablesFilterAttribute": {
"target_attr_name": "vars_required",
"source_attr_name": "vars",
"required": True,
}
},
{
"VariablesFilterAttribute": {
"target_attr_name": "vars_optional",
"source_attr_name": "vars",
"required": False,
}
},
"FreckletsTemplateKeysAttribute",
"CliArgumentsAttribute",
"TaskListAttribute",
"TaskListResolvedAttribute",
],
"ting_id_attr": "frecklet_name",
"mixins": [FrecklecutableMixin, JinjaTemplateMixin],
"loaders": {
"frecklet_files": {
"class": "ting.tings.FileTings",
"load_config": {"folder_load_file_match_regex": "\\.frecklet$"},
"attributes": [
{
"DictContentAttribute": {
"dict_name": "_metadata_raw",
"source_attr_name": "ting_content",
}
},
"FileStringContentAttribute",
{
"MirrorAttribute": {
"source_attr_name": "filename_no_ext",
"target_attr_name": "frecklet_name",
}
},
],
},
"frecklet_file": {
"class": "ting.tings.FileTings",
"load_config": {},
"attributes": [
{
"DictContentAttribute": {
"dict_name": "_metadata_raw",
"source_attr_name": "ting_content",
}
},
"FileStringContentAttribute",
{
"MirrorAttribute": {
"source_attr_name": "full_path",
"target_attr_name": "frecklet_name",
}
},
],
},
"frecklet_dicts": {
"class": "ting.tings.DictTings",
"load_config": {},
"attributes": [],
},
},
}