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

I've corrected some obvious issues, and we can fix others over time. Mypy tests are currently broken, as adding the type hints has increased mypy's testing surface.
117 lines
3.4 KiB
Python
117 lines
3.4 KiB
Python
from .template import Template
|
|
import os.path
|
|
import re
|
|
from typing import Any
|
|
|
|
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 = None
|
|
|
|
# Absolute path to the template itself. Pystache will try to guess
|
|
# if it's not provided.
|
|
template_file = None
|
|
|
|
# Contents of the template.
|
|
template = None
|
|
|
|
# Character encoding of the template file. If None, Pystache will not
|
|
# do any decoding of the template.
|
|
template_encoding = 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()
|