summaryrefslogtreecommitdiff
path: root/pystache/locator.py
diff options
context:
space:
mode:
Diffstat (limited to 'pystache/locator.py')
-rw-r--r--pystache/locator.py152
1 files changed, 152 insertions, 0 deletions
diff --git a/pystache/locator.py b/pystache/locator.py
new file mode 100644
index 0000000..a1f06db
--- /dev/null
+++ b/pystache/locator.py
@@ -0,0 +1,152 @@
+# coding: utf-8
+
+"""
+This module provides a Locator class for finding template files.
+
+"""
+
+import os
+import re
+import sys
+
+from pystache import defaults
+
+
+class Locator(object):
+
+ def __init__(self, extension=None):
+ """
+ Construct a template locator.
+
+ Arguments:
+
+ extension: the template file extension. Pass False for no
+ extension (i.e. 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:
+ # TODO: we should probably raise an exception of our own type.
+ raise IOError('Template file %s not found in directories: %s' %
+ (repr(file_name), repr(search_dirs)))
+
+ return path
+
+ def find_name(self, template_name, search_dirs):
+ """
+ Return the path to a template with the given name.
+
+ """
+ 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