Team Ai
Datasetpublic

codekingpro/portable-devtools

sourceHugging Faceupdated 5mo agoView on Hugging Face
1likes14kdownloads
lookup.py362 linesDownload Raw Back to mako
1# mako/lookup.py2# Copyright 2006-2024 the Mako authors and contributors <see AUTHORS file>3#4# This module is part of Mako and is released under5# the MIT License: http://www.opensource.org/licenses/mit-license.php6 7import os8import posixpath9import re10import stat11import threading12 13from mako import exceptions14from mako import util15from mako.template import Template16 17 18class TemplateCollection:19 20    """Represent a collection of :class:`.Template` objects,21    identifiable via URI.22 23    A :class:`.TemplateCollection` is linked to the usage of24    all template tags that address other templates, such25    as ``<%include>``, ``<%namespace>``, and ``<%inherit>``.26    The ``file`` attribute of each of those tags refers27    to a string URI that is passed to that :class:`.Template`28    object's :class:`.TemplateCollection` for resolution.29 30    :class:`.TemplateCollection` is an abstract class,31    with the usual default implementation being :class:`.TemplateLookup`.32 33    """34 35    def has_template(self, uri):36        """Return ``True`` if this :class:`.TemplateLookup` is37        capable of returning a :class:`.Template` object for the38        given ``uri``.39 40        :param uri: String URI of the template to be resolved.41 42        """43        try:44            self.get_template(uri)45            return True46        except exceptions.TemplateLookupException:47            return False48 49    def get_template(self, uri, relativeto=None):50        """Return a :class:`.Template` object corresponding to the given51        ``uri``.52 53        The default implementation raises54        :class:`.NotImplementedError`. Implementations should55        raise :class:`.TemplateLookupException` if the given ``uri``56        cannot be resolved.57 58        :param uri: String URI of the template to be resolved.59        :param relativeto: if present, the given ``uri`` is assumed to60         be relative to this URI.61 62        """63        raise NotImplementedError()64 65    def filename_to_uri(self, uri, filename):66        """Convert the given ``filename`` to a URI relative to67        this :class:`.TemplateCollection`."""68 69        return uri70 71    def adjust_uri(self, uri, filename):72        """Adjust the given ``uri`` based on the calling ``filename``.73 74        When this method is called from the runtime, the75        ``filename`` parameter is taken directly to the ``filename``76        attribute of the calling template. Therefore a custom77        :class:`.TemplateCollection` subclass can place any string78        identifier desired in the ``filename`` parameter of the79        :class:`.Template` objects it constructs and have them come back80        here.81 82        """83        return uri84 85 86class TemplateLookup(TemplateCollection):87 88    """Represent a collection of templates that locates template source files89    from the local filesystem.90 91    The primary argument is the ``directories`` argument, the list of92    directories to search:93 94    .. sourcecode:: python95 96        lookup = TemplateLookup(["/path/to/templates"])97        some_template = lookup.get_template("/index.html")98 99    The :class:`.TemplateLookup` can also be given :class:`.Template` objects100    programatically using :meth:`.put_string` or :meth:`.put_template`:101 102    .. sourcecode:: python103 104        lookup = TemplateLookup()105        lookup.put_string("base.html", '''106            <html><body>${self.next()}</body></html>107        ''')108        lookup.put_string("hello.html", '''109            <%include file='base.html'/>110 111            Hello, world !112        ''')113 114 115    :param directories: A list of directory names which will be116     searched for a particular template URI. The URI is appended117     to each directory and the filesystem checked.118 119    :param collection_size: Approximate size of the collection used120     to store templates. If left at its default of ``-1``, the size121     is unbounded, and a plain Python dictionary is used to122     relate URI strings to :class:`.Template` instances.123     Otherwise, a least-recently-used cache object is used which124     will maintain the size of the collection approximately to125     the number given.126 127    :param filesystem_checks: When at its default value of ``True``,128     each call to :meth:`.TemplateLookup.get_template()` will129     compare the filesystem last modified time to the time in130     which an existing :class:`.Template` object was created.131     This allows the :class:`.TemplateLookup` to regenerate a132     new :class:`.Template` whenever the original source has133     been updated. Set this to ``False`` for a very minor134     performance increase.135 136    :param modulename_callable: A callable which, when present,137     is passed the path of the source file as well as the138     requested URI, and then returns the full path of the139     generated Python module file. This is used to inject140     alternate schemes for Python module location. If left at141     its default of ``None``, the built in system of generation142     based on ``module_directory`` plus ``uri`` is used.143 144    All other keyword parameters available for145    :class:`.Template` are mirrored here. When new146    :class:`.Template` objects are created, the keywords147    established with this :class:`.TemplateLookup` are passed on148    to each new :class:`.Template`.149 150    """151 152    def __init__(153        self,154        directories=None,155        module_directory=None,156        filesystem_checks=True,157        collection_size=-1,158        format_exceptions=False,159        error_handler=None,160        output_encoding=None,161        encoding_errors="strict",162        cache_args=None,163        cache_impl="beaker",164        cache_enabled=True,165        cache_type=None,166        cache_dir=None,167        cache_url=None,168        modulename_callable=None,169        module_writer=None,170        default_filters=None,171        buffer_filters=(),172        strict_undefined=False,173        imports=None,174        future_imports=None,175        enable_loop=True,176        input_encoding=None,177        preprocessor=None,178        lexer_cls=None,179        include_error_handler=None,180    ):181        self.directories = [182            posixpath.normpath(d) for d in util.to_list(directories, ())183        ]184        self.module_directory = module_directory185        self.modulename_callable = modulename_callable186        self.filesystem_checks = filesystem_checks187        self.collection_size = collection_size188 189        if cache_args is None:190            cache_args = {}191        # transfer deprecated cache_* args192        if cache_dir:193            cache_args.setdefault("dir", cache_dir)194        if cache_url:195            cache_args.setdefault("url", cache_url)196        if cache_type:197            cache_args.setdefault("type", cache_type)198 199        self.template_args = {200            "format_exceptions": format_exceptions,201            "error_handler": error_handler,202            "include_error_handler": include_error_handler,203            "output_encoding": output_encoding,204            "cache_impl": cache_impl,205            "encoding_errors": encoding_errors,206            "input_encoding": input_encoding,207            "module_directory": module_directory,208            "module_writer": module_writer,209            "cache_args": cache_args,210            "cache_enabled": cache_enabled,211            "default_filters": default_filters,212            "buffer_filters": buffer_filters,213            "strict_undefined": strict_undefined,214            "imports": imports,215            "future_imports": future_imports,216            "enable_loop": enable_loop,217            "preprocessor": preprocessor,218            "lexer_cls": lexer_cls,219        }220 221        if collection_size == -1:222            self._collection = {}223            self._uri_cache = {}224        else:225            self._collection = util.LRUCache(collection_size)226            self._uri_cache = util.LRUCache(collection_size)227        self._mutex = threading.Lock()228 229    def get_template(self, uri):230        """Return a :class:`.Template` object corresponding to the given231        ``uri``.232 233        .. note:: The ``relativeto`` argument is not supported here at234           the moment.235 236        """237 238        try:239            if self.filesystem_checks:240                return self._check(uri, self._collection[uri])241            else:242                return self._collection[uri]243        except KeyError as e:244            u = re.sub(r"^\/+", "", uri)245            for dir_ in self.directories:246                # make sure the path seperators are posix - os.altsep is empty247                # on POSIX and cannot be used.248                dir_ = dir_.replace(os.path.sep, posixpath.sep)249                srcfile = posixpath.normpath(posixpath.join(dir_, u))250                if os.path.isfile(srcfile):251                    return self._load(srcfile, uri)252            else:253                raise exceptions.TopLevelLookupException(254                    "Can't locate template for uri %r" % uri255                ) from e256 257    def adjust_uri(self, uri, relativeto):258        """Adjust the given ``uri`` based on the given relative URI."""259 260        key = (uri, relativeto)261        if key in self._uri_cache:262            return self._uri_cache[key]263 264        if uri[0] == "/":265            v = self._uri_cache[key] = uri266        elif relativeto is not None:267            v = self._uri_cache[key] = posixpath.join(268                posixpath.dirname(relativeto), uri269            )270        else:271            v = self._uri_cache[key] = "/" + uri272        return v273 274    def filename_to_uri(self, filename):275        """Convert the given ``filename`` to a URI relative to276        this :class:`.TemplateCollection`."""277 278        try:279            return self._uri_cache[filename]280        except KeyError:281            value = self._relativeize(filename)282            self._uri_cache[filename] = value283            return value284 285    def _relativeize(self, filename):286        """Return the portion of a filename that is 'relative'287        to the directories in this lookup.288 289        """290 291        filename = posixpath.normpath(filename)292        for dir_ in self.directories:293            if filename[0 : len(dir_)] == dir_:294                return filename[len(dir_) :]295        else:296            return None297 298    def _load(self, filename, uri):299        self._mutex.acquire()300        try:301            try:302                # try returning from collection one303                # more time in case concurrent thread already loaded304                return self._collection[uri]305            except KeyError:306                pass307            try:308                if self.modulename_callable is not None:309                    module_filename = self.modulename_callable(filename, uri)310                else:311                    module_filename = None312                self._collection[uri] = template = Template(313                    uri=uri,314                    filename=posixpath.normpath(filename),315                    lookup=self,316                    module_filename=module_filename,317                    **self.template_args,318                )319                return template320            except:321                # if compilation fails etc, ensure322                # template is removed from collection,323                # re-raise324                self._collection.pop(uri, None)325                raise326        finally:327            self._mutex.release()328 329    def _check(self, uri, template):330        if template.filename is None:331            return template332 333        try:334            template_stat = os.stat(template.filename)335            if template.module._modified_time >= template_stat[stat.ST_MTIME]:336                return template337            self._collection.pop(uri, None)338            return self._load(template.filename, uri)339        except OSError as e:340            self._collection.pop(uri, None)341            raise exceptions.TemplateLookupException(342                "Can't locate template for uri %r" % uri343            ) from e344 345    def put_string(self, uri, text):346        """Place a new :class:`.Template` object into this347        :class:`.TemplateLookup`, based on the given string of348        ``text``.349 350        """351        self._collection[uri] = Template(352            text, lookup=self, uri=uri, **self.template_args353        )354 355    def put_template(self, uri, template):356        """Place a new :class:`.Template` object into this357        :class:`.TemplateLookup`, based on the given358        :class:`.Template` object.359 360        """361        self._collection[uri] = template362 
codekingpro/portable-devtools · Team Ai