Added Mako report library
[odoo/odoo.git] / bin / mako / lookup.py
1 # lookup.py
2 # Copyright (C) 2006, 2007, 2008 Michael Bayer mike_mp@zzzcomputing.com
3 #
4 # This module is part of Mako and is released under
5 # the MIT License: http://www.opensource.org/licenses/mit-license.php
6
7 import os, stat, posixpath, re
8 from mako import exceptions, util
9 from mako.template import Template
10
11 try:
12     import threading
13 except:
14     import dummy_threading as threading
15     
16 class TemplateCollection(object):
17     def has_template(self, uri):
18         try:
19             self.get_template(uri)
20             return True
21         except exceptions.TemplateLookupException, e:
22             return False
23     def get_template(self, uri, relativeto=None):
24         raise NotImplementedError()
25     def filename_to_uri(self, uri, filename):
26         """convert the given filename to a uri relative to this TemplateCollection."""
27         return uri
28         
29     def adjust_uri(self, uri, filename):
30         """adjust the given uri based on the calling filename.
31         
32         when this method is called from the runtime, the 'filename' parameter 
33         is taken directly to the 'filename' attribute of the calling 
34         template.  Therefore a custom TemplateCollection subclass can place any string 
35         identifier desired in the "filename" parameter of the Template objects it constructs
36         and have them come back here."""
37         return uri
38         
39 class TemplateLookup(TemplateCollection):
40     def __init__(self, directories=None, module_directory=None, filesystem_checks=True, collection_size=-1, format_exceptions=False, 
41     error_handler=None, disable_unicode=False, output_encoding=None, encoding_errors='strict', cache_type=None, cache_dir=None, cache_url=None, 
42     cache_enabled=True, modulename_callable=None, default_filters=None, buffer_filters=[], imports=None, input_encoding=None, preprocessor=None):
43         if isinstance(directories, basestring):
44             directories = [directories]        
45         self.directories = [posixpath.normpath(d) for d in directories or []]
46         self.module_directory = module_directory
47         self.modulename_callable = modulename_callable
48         self.filesystem_checks = filesystem_checks
49         self.collection_size = collection_size
50         self.template_args = {
51             'format_exceptions':format_exceptions, 
52             'error_handler':error_handler, 
53             'disable_unicode':disable_unicode, 
54             'output_encoding':output_encoding, 
55             'encoding_errors':encoding_errors, 
56             'input_encoding':input_encoding, 
57             'module_directory':module_directory, 
58             'cache_type':cache_type, 
59             'cache_dir':cache_dir or module_directory, 
60             'cache_url':cache_url, 
61             'cache_enabled':cache_enabled, 
62             'default_filters':default_filters, 
63             'buffer_filters':buffer_filters,  
64             'imports':imports, 
65             'preprocessor':preprocessor}
66         if collection_size == -1:
67             self.__collection = {}
68             self._uri_cache = {}
69         else:
70             self.__collection = util.LRUCache(collection_size)
71             self._uri_cache = util.LRUCache(collection_size)
72         self._mutex = threading.Lock()
73         
74     def get_template(self, uri):
75         try:
76             if self.filesystem_checks:
77                 return self.__check(uri, self.__collection[uri])
78             else:
79                 return self.__collection[uri]
80         except KeyError:
81             u = re.sub(r'^\/+', '', uri)
82             for dir in self.directories:
83                 srcfile = posixpath.normpath(posixpath.join(dir, u))
84                 if os.path.exists(srcfile):
85                     return self.__load(srcfile, uri)
86             else:
87                 raise exceptions.TopLevelLookupException("Cant locate template for uri '%s'" % uri)
88
89     def adjust_uri(self, uri, relativeto):
90         """adjust the given uri based on the calling filename."""
91         
92         if uri[0] != '/':
93             if relativeto is not None:
94                 return posixpath.join(posixpath.dirname(relativeto), uri)
95             else:
96                 return '/' + uri
97         else:
98             return uri
99             
100     
101     def filename_to_uri(self, filename):
102         try:
103             return self._uri_cache[filename]
104         except KeyError:
105             value = self.__relativeize(filename)
106             self._uri_cache[filename] = value
107             return value
108                     
109     def __relativeize(self, filename):
110         """return the portion of a filename that is 'relative' to the directories in this lookup."""
111         filename = posixpath.normpath(filename)
112         for dir in self.directories:
113             if filename[0:len(dir)] == dir:
114                 return filename[len(dir):]
115         else:
116             return None
117             
118     def __load(self, filename, uri):
119         self._mutex.acquire()
120         try:
121             try:
122                 # try returning from collection one more time in case concurrent thread already loaded
123                 return self.__collection[uri]
124             except KeyError:
125                 pass
126             try:
127                 self.__collection[uri] = Template(uri=uri, filename=posixpath.normpath(filename), lookup=self, module_filename=(self.modulename_callable is not None and self.modulename_callable(filename, uri) or None), **self.template_args)
128                 return self.__collection[uri]
129             except:
130                 self.__collection.pop(uri, None)
131                 raise
132         finally:
133             self._mutex.release()
134             
135     def __check(self, uri, template):
136         if template.filename is None:
137             return template
138         if not os.path.exists(template.filename):
139             self.__collection.pop(uri, None)
140             raise exceptions.TemplateLookupException("Cant locate template for uri '%s'" % uri)
141         elif template.module._modified_time < os.stat(template.filename)[stat.ST_MTIME]:
142             self.__collection.pop(uri, None)
143             return self.__load(template.filename, uri)
144         else:
145             return template
146             
147     def put_string(self, uri, text):
148         self.__collection[uri] = Template(text, lookup=self, uri=uri, **self.template_args)
149     def put_template(self, uri, template):
150         self.__collection[uri] = template
151