mirror of
https://github.com/ankitects/anki.git
synced 2025-09-19 14:32:22 -04:00

Earlier today I pushed a change that split this code up into multiple repos, but that has proved to complicate things too much. So we're back to a single repo, except the individual submodules are better separated than they were before. The README files need updating again; I will push them out soon. Aside from splitting out the different modules, the sound code has moved from from anki to aqt.
118 lines
3.5 KiB
Python
118 lines
3.5 KiB
Python
import os.path
|
|
import re
|
|
from typing import Any
|
|
|
|
from .template import Template
|
|
|
|
|
|
class View:
|
|
# Path where this view's template(s) live
|
|
template_path = "."
|
|
|
|
# Extension for templates
|
|
template_extension = "mustache"
|
|
|
|
# The name of this template. If none is given the View will try
|
|
# to infer it based on the class name.
|
|
template_name: str = None
|
|
|
|
# Absolute path to the template itself. Pystache will try to guess
|
|
# if it's not provided.
|
|
template_file: str = None
|
|
|
|
# Contents of the template.
|
|
template: str = None
|
|
|
|
# Character encoding of the template file. If None, Pystache will not
|
|
# do any decoding of the template.
|
|
template_encoding: str = None
|
|
|
|
def __init__(self, template=None, context=None, **kwargs) -> None:
|
|
self.template = template
|
|
self.context = context or {}
|
|
|
|
# If the context we're handed is a View, we want to inherit
|
|
# its settings.
|
|
if isinstance(context, View):
|
|
self.inherit_settings(context)
|
|
|
|
if kwargs:
|
|
self.context.update(kwargs)
|
|
|
|
def inherit_settings(self, view) -> None:
|
|
"""Given another View, copies its settings."""
|
|
if view.template_path:
|
|
self.template_path = view.template_path
|
|
|
|
if view.template_name:
|
|
self.template_name = view.template_name
|
|
|
|
def load_template(self) -> Any:
|
|
if self.template:
|
|
return self.template
|
|
|
|
if self.template_file:
|
|
return self._load_template()
|
|
|
|
name = self.get_template_name() + "." + self.template_extension
|
|
|
|
if isinstance(self.template_path, str):
|
|
self.template_file = os.path.join(self.template_path, name)
|
|
return self._load_template()
|
|
|
|
for path in self.template_path:
|
|
self.template_file = os.path.join(path, name)
|
|
if os.path.exists(self.template_file):
|
|
return self._load_template()
|
|
|
|
raise IOError('"%s" not found in "%s"' % (name, ":".join(self.template_path),))
|
|
|
|
def _load_template(self) -> str:
|
|
f = open(self.template_file, "r")
|
|
try:
|
|
template = f.read()
|
|
if self.template_encoding and isinstance(template, bytes):
|
|
template = str(template, self.template_encoding)
|
|
finally:
|
|
f.close()
|
|
return template
|
|
|
|
def get_template_name(self, name=None) -> Any:
|
|
"""TemplatePartial => template_partial
|
|
Takes a string but defaults to using the current class' name or
|
|
the `template_name` attribute
|
|
"""
|
|
if self.template_name:
|
|
return self.template_name
|
|
|
|
if not name:
|
|
name = self.__class__.__name__
|
|
|
|
def repl(match):
|
|
return "_" + match.group(0).lower()
|
|
|
|
return re.sub("[A-Z]", repl, name)[1:]
|
|
|
|
def __contains__(self, needle) -> bool:
|
|
return needle in self.context or hasattr(self, needle)
|
|
|
|
def __getitem__(self, attr) -> Any:
|
|
val = self.get(attr, None)
|
|
if not val:
|
|
raise KeyError("No such key.")
|
|
return val
|
|
|
|
def get(self, attr, default) -> Any:
|
|
attr = self.context.get(attr, getattr(self, attr, default))
|
|
|
|
if hasattr(attr, "__call__"):
|
|
return attr()
|
|
else:
|
|
return attr
|
|
|
|
def render(self, encoding=None) -> str:
|
|
template = self.load_template()
|
|
return Template(template, self).render(encoding=encoding)
|
|
|
|
def __str__(self) -> str:
|
|
return self.render()
|