| # coding: utf-8 |
| |
| """ |
| This module provides a Locator class for finding template files. |
| |
| """ |
| |
| import os |
| import re |
| import sys |
| |
| from pystache.common import TemplateNotFoundError |
| from pystache import defaults |
| |
| |
| class Locator(object): |
| |
| def __init__(self, extension=None): |
| """ |
| Construct a template locator. |
| |
| Arguments: |
| |
| extension: the template file extension, without the leading dot. |
| Pass False for no extension (e.g. to use extensionless template |
| files). Defaults to the package default. |
| |
| """ |
| if extension is None: |
| extension = defaults.TEMPLATE_EXTENSION |
| |
| self.template_extension = extension |
| |
| def get_object_directory(self, obj): |
| """ |
| Return the directory containing an object's defining class. |
| |
| Returns None if there is no such directory, for example if the |
| class was defined in an interactive Python session, or in a |
| doctest that appears in a text file (rather than a Python file). |
| |
| """ |
| if not hasattr(obj, '__module__'): |
| return None |
| |
| module = sys.modules[obj.__module__] |
| |
| if not hasattr(module, '__file__'): |
| # TODO: add a unit test for this case. |
| return None |
| |
| path = module.__file__ |
| |
| return os.path.dirname(path) |
| |
| def make_template_name(self, obj): |
| """ |
| Return the canonical template name for an object instance. |
| |
| This method converts Python-style class names (PEP 8's recommended |
| CamelCase, aka CapWords) to lower_case_with_underscords. Here |
| is an example with code: |
| |
| >>> class HelloWorld(object): |
| ... pass |
| >>> hi = HelloWorld() |
| >>> |
| >>> locator = Locator() |
| >>> locator.make_template_name(hi) |
| 'hello_world' |
| |
| """ |
| template_name = obj.__class__.__name__ |
| |
| def repl(match): |
| return '_' + match.group(0).lower() |
| |
| return re.sub('[A-Z]', repl, template_name)[1:] |
| |
| def make_file_name(self, template_name, template_extension=None): |
| """ |
| Generate and return the file name for the given template name. |
| |
| Arguments: |
| |
| template_extension: defaults to the instance's extension. |
| |
| """ |
| file_name = template_name |
| |
| if template_extension is None: |
| template_extension = self.template_extension |
| |
| if template_extension is not False: |
| file_name += os.path.extsep + template_extension |
| |
| return file_name |
| |
| def _find_path(self, search_dirs, file_name): |
| """ |
| Search for the given file, and return the path. |
| |
| Returns None if the file is not found. |
| |
| """ |
| for dir_path in search_dirs: |
| file_path = os.path.join(dir_path, file_name) |
| if os.path.exists(file_path): |
| return file_path |
| |
| return None |
| |
| def _find_path_required(self, search_dirs, file_name): |
| """ |
| Return the path to a template with the given file name. |
| |
| """ |
| path = self._find_path(search_dirs, file_name) |
| |
| if path is None: |
| raise TemplateNotFoundError('File %s not found in dirs: %s' % |
| (repr(file_name), repr(search_dirs))) |
| |
| return path |
| |
| def find_file(self, file_name, search_dirs): |
| """ |
| Return the path to a template with the given file name. |
| |
| Arguments: |
| |
| file_name: the file name of the template. |
| |
| search_dirs: the list of directories in which to search. |
| |
| """ |
| return self._find_path_required(search_dirs, file_name) |
| |
| def find_name(self, template_name, search_dirs): |
| """ |
| Return the path to a template with the given name. |
| |
| Arguments: |
| |
| template_name: the name of the template. |
| |
| search_dirs: the list of directories in which to search. |
| |
| """ |
| file_name = self.make_file_name(template_name) |
| |
| return self._find_path_required(search_dirs, file_name) |
| |
| def find_object(self, obj, search_dirs, file_name=None): |
| """ |
| Return the path to a template associated with the given object. |
| |
| """ |
| if file_name is None: |
| # TODO: should we define a make_file_name() method? |
| template_name = self.make_template_name(obj) |
| file_name = self.make_file_name(template_name) |
| |
| dir_path = self.get_object_directory(obj) |
| |
| if dir_path is not None: |
| search_dirs = [dir_path] + search_dirs |
| |
| path = self._find_path_required(search_dirs, file_name) |
| |
| return path |