python/pystache/pystache/locator.py
author Phil Ringnalda <philringnalda@gmail.com>
Fri, 27 Nov 2015 21:39:23 -0800
changeset 310469 c95f8e8955b0ee3a807d1d8f7887e6286580a0fa
parent 254434 5edda558c6555e518d3579f89854b556c453c66a
permissions -rw-r--r--
Back out 8 changesets (bug 1223573) for Win7 PGO xperf unexpected access failures CLOSED TREE Backed out changeset 19876a153a00 (bug 1223573) Backed out changeset 14251062e347 (bug 1223573) Backed out changeset 081b0af71d6e (bug 1223573) Backed out changeset 21ebe3534e58 (bug 1223573) Backed out changeset d6754894897c (bug 1223573) Backed out changeset 1b4d6308002e (bug 1223573) Backed out changeset a13b3bba5529 (bug 1223573) Backed out changeset f3ccccf5b6fe (bug 1223573)

# 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