Team Ai
Datasetpublic

codekingpro/portable-devtools

sourceHugging Faceupdated 5mo agoView on Hugging Face
1likes14kdownloads
runtime.py969 linesDownload Raw Back to mako
1# mako/runtime.py2# Copyright 2006-2020 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 7"""provides runtime services for templates, including Context,8Namespace, and various helper functions."""9 10import builtins11import functools12import sys13 14from mako import compat15from mako import exceptions16from mako import util17 18 19class Context:20 21    """Provides runtime namespace, output buffer, and various22    callstacks for templates.23 24    See :ref:`runtime_toplevel` for detail on the usage of25    :class:`.Context`.26 27    """28 29    def __init__(self, buffer, **data):30        self._buffer_stack = [buffer]31 32        self._data = data33 34        self._kwargs = data.copy()35        self._with_template = None36        self._outputting_as_unicode = None37        self.namespaces = {}38 39        # "capture" function which proxies to the40        # generic "capture" function41        self._data["capture"] = functools.partial(capture, self)42 43        # "caller" stack used by def calls with content44        self.caller_stack = self._data["caller"] = CallerStack()45 46    def _set_with_template(self, t):47        self._with_template = t48        illegal_names = t.reserved_names.intersection(self._data)49        if illegal_names:50            raise exceptions.NameConflictError(51                "Reserved words passed to render(): %s"52                % ", ".join(illegal_names)53            )54 55    @property56    def lookup(self):57        """Return the :class:`.TemplateLookup` associated58        with this :class:`.Context`.59 60        """61        return self._with_template.lookup62 63    @property64    def kwargs(self):65        """Return the dictionary of top level keyword arguments associated66        with this :class:`.Context`.67 68        This dictionary only includes the top-level arguments passed to69        :meth:`.Template.render`.  It does not include names produced within70        the template execution such as local variable names or special names71        such as ``self``, ``next``, etc.72 73        The purpose of this dictionary is primarily for the case that74        a :class:`.Template` accepts arguments via its ``<%page>`` tag,75        which are normally expected to be passed via :meth:`.Template.render`,76        except the template is being called in an inheritance context,77        using the ``body()`` method.   :attr:`.Context.kwargs` can then be78        used to propagate these arguments to the inheriting template::79 80            ${next.body(**context.kwargs)}81 82        """83        return self._kwargs.copy()84 85    def push_caller(self, caller):86        """Push a ``caller`` callable onto the callstack for87        this :class:`.Context`."""88 89        self.caller_stack.append(caller)90 91    def pop_caller(self):92        """Pop a ``caller`` callable onto the callstack for this93        :class:`.Context`."""94 95        del self.caller_stack[-1]96 97    def keys(self):98        """Return a list of all names established in this :class:`.Context`."""99 100        return list(self._data.keys())101 102    def __getitem__(self, key):103        if key in self._data:104            return self._data[key]105        else:106            return builtins.__dict__[key]107 108    def _push_writer(self):109        """push a capturing buffer onto this Context and return110        the new writer function."""111 112        buf = util.FastEncodingBuffer()113        self._buffer_stack.append(buf)114        return buf.write115 116    def _pop_buffer_and_writer(self):117        """pop the most recent capturing buffer from this Context118        and return the current writer after the pop.119 120        """121 122        buf = self._buffer_stack.pop()123        return buf, self._buffer_stack[-1].write124 125    def _push_buffer(self):126        """push a capturing buffer onto this Context."""127 128        self._push_writer()129 130    def _pop_buffer(self):131        """pop the most recent capturing buffer from this Context."""132 133        return self._buffer_stack.pop()134 135    def get(self, key, default=None):136        """Return a value from this :class:`.Context`."""137 138        return self._data.get(key, builtins.__dict__.get(key, default))139 140    def write(self, string):141        """Write a string to this :class:`.Context` object's142        underlying output buffer."""143 144        self._buffer_stack[-1].write(string)145 146    def writer(self):147        """Return the current writer function."""148 149        return self._buffer_stack[-1].write150 151    def _copy(self):152        c = Context.__new__(Context)153        c._buffer_stack = self._buffer_stack154        c._data = self._data.copy()155        c._kwargs = self._kwargs156        c._with_template = self._with_template157        c._outputting_as_unicode = self._outputting_as_unicode158        c.namespaces = self.namespaces159        c.caller_stack = self.caller_stack160        return c161 162    def _locals(self, d):163        """Create a new :class:`.Context` with a copy of this164        :class:`.Context`'s current state,165        updated with the given dictionary.166 167        The :attr:`.Context.kwargs` collection remains168        unaffected.169 170 171        """172 173        if not d:174            return self175        c = self._copy()176        c._data.update(d)177        return c178 179    def _clean_inheritance_tokens(self):180        """create a new copy of this :class:`.Context`. with181        tokens related to inheritance state removed."""182 183        c = self._copy()184        x = c._data185        x.pop("self", None)186        x.pop("parent", None)187        x.pop("next", None)188        return c189 190 191class CallerStack(list):192    def __init__(self):193        self.nextcaller = None194 195    def __nonzero__(self):196        return self.__bool__()197 198    def __bool__(self):199        return len(self) and self._get_caller() and True or False200 201    def _get_caller(self):202        # this method can be removed once203        # codegen MAGIC_NUMBER moves past 7204        return self[-1]205 206    def __getattr__(self, key):207        return getattr(self._get_caller(), key)208 209    def _push_frame(self):210        frame = self.nextcaller or None211        self.append(frame)212        self.nextcaller = None213        return frame214 215    def _pop_frame(self):216        self.nextcaller = self.pop()217 218 219class Undefined:220 221    """Represents an undefined value in a template.222 223    All template modules have a constant value224    ``UNDEFINED`` present which is an instance of this225    object.226 227    """228 229    def __str__(self):230        raise NameError("Undefined")231 232    def __nonzero__(self):233        return self.__bool__()234 235    def __bool__(self):236        return False237 238 239UNDEFINED = Undefined()240STOP_RENDERING = ""241 242 243class LoopStack:244 245    """a stack for LoopContexts that implements the context manager protocol246    to automatically pop off the top of the stack on context exit247    """248 249    def __init__(self):250        self.stack = []251 252    def _enter(self, iterable):253        self._push(iterable)254        return self._top255 256    def _exit(self):257        self._pop()258        return self._top259 260    @property261    def _top(self):262        if self.stack:263            return self.stack[-1]264        else:265            return self266 267    def _pop(self):268        return self.stack.pop()269 270    def _push(self, iterable):271        new = LoopContext(iterable)272        if self.stack:273            new.parent = self.stack[-1]274        return self.stack.append(new)275 276    def __getattr__(self, key):277        raise exceptions.RuntimeException("No loop context is established")278 279    def __iter__(self):280        return iter(self._top)281 282 283class LoopContext:284 285    """A magic loop variable.286    Automatically accessible in any ``% for`` block.287 288    See the section :ref:`loop_context` for usage289    notes.290 291    :attr:`parent` -> :class:`.LoopContext` or ``None``292        The parent loop, if one exists.293    :attr:`index` -> `int`294        The 0-based iteration count.295    :attr:`reverse_index` -> `int`296        The number of iterations remaining.297    :attr:`first` -> `bool`298        ``True`` on the first iteration, ``False`` otherwise.299    :attr:`last` -> `bool`300        ``True`` on the last iteration, ``False`` otherwise.301    :attr:`even` -> `bool`302        ``True`` when ``index`` is even.303    :attr:`odd` -> `bool`304        ``True`` when ``index`` is odd.305    """306 307    def __init__(self, iterable):308        self._iterable = iterable309        self.index = 0310        self.parent = None311 312    def __iter__(self):313        for i in self._iterable:314            yield i315            self.index += 1316 317    @util.memoized_instancemethod318    def __len__(self):319        return len(self._iterable)320 321    @property322    def reverse_index(self):323        return len(self) - self.index - 1324 325    @property326    def first(self):327        return self.index == 0328 329    @property330    def last(self):331        return self.index == len(self) - 1332 333    @property334    def even(self):335        return not self.odd336 337    @property338    def odd(self):339        return bool(self.index % 2)340 341    def cycle(self, *values):342        """Cycle through values as the loop progresses."""343        if not values:344            raise ValueError("You must provide values to cycle through")345        return values[self.index % len(values)]346 347 348class _NSAttr:349    def __init__(self, parent):350        self.__parent = parent351 352    def __getattr__(self, key):353        ns = self.__parent354        while ns:355            if hasattr(ns.module, key):356                return getattr(ns.module, key)357            else:358                ns = ns.inherits359        raise AttributeError(key)360 361 362class Namespace:363 364    """Provides access to collections of rendering methods, which365    can be local, from other templates, or from imported modules.366 367    To access a particular rendering method referenced by a368    :class:`.Namespace`, use plain attribute access:369 370    .. sourcecode:: mako371 372      ${some_namespace.foo(x, y, z)}373 374    :class:`.Namespace` also contains several built-in attributes375    described here.376 377    """378 379    def __init__(380        self,381        name,382        context,383        callables=None,384        inherits=None,385        populate_self=True,386        calling_uri=None,387    ):388        self.name = name389        self.context = context390        self.inherits = inherits391        if callables is not None:392            self.callables = {c.__name__: c for c in callables}393 394    callables = ()395 396    module = None397    """The Python module referenced by this :class:`.Namespace`.398 399    If the namespace references a :class:`.Template`, then400    this module is the equivalent of ``template.module``,401    i.e. the generated module for the template.402 403    """404 405    template = None406    """The :class:`.Template` object referenced by this407        :class:`.Namespace`, if any.408 409    """410 411    context = None412    """The :class:`.Context` object for this :class:`.Namespace`.413 414    Namespaces are often created with copies of contexts that415    contain slightly different data, particularly in inheritance416    scenarios. Using the :class:`.Context` off of a :class:`.Namespace` one417    can traverse an entire chain of templates that inherit from418    one-another.419 420    """421 422    filename = None423    """The path of the filesystem file used for this424    :class:`.Namespace`'s module or template.425 426    If this is a pure module-based427    :class:`.Namespace`, this evaluates to ``module.__file__``. If a428    template-based namespace, it evaluates to the original429    template file location.430 431    """432 433    uri = None434    """The URI for this :class:`.Namespace`'s template.435 436    I.e. whatever was sent to :meth:`.TemplateLookup.get_template()`.437 438    This is the equivalent of :attr:`.Template.uri`.439 440    """441 442    _templateuri = None443 444    @util.memoized_property445    def attr(self):446        """Access module level attributes by name.447 448        This accessor allows templates to supply "scalar"449        attributes which are particularly handy in inheritance450        relationships.451 452        .. seealso::453 454            :ref:`inheritance_attr`455 456            :ref:`namespace_attr_for_includes`457 458        """459        return _NSAttr(self)460 461    def get_namespace(self, uri):462        """Return a :class:`.Namespace` corresponding to the given ``uri``.463 464        If the given ``uri`` is a relative URI (i.e. it does not465        contain a leading slash ``/``), the ``uri`` is adjusted to466        be relative to the ``uri`` of the namespace itself. This467        method is therefore mostly useful off of the built-in468        ``local`` namespace, described in :ref:`namespace_local`.469 470        In471        most cases, a template wouldn't need this function, and472        should instead use the ``<%namespace>`` tag to load473        namespaces. However, since all ``<%namespace>`` tags are474        evaluated before the body of a template ever runs,475        this method can be used to locate namespaces using476        expressions that were generated within the body code of477        the template, or to conditionally use a particular478        namespace.479 480        """481        key = (self, uri)482        if key in self.context.namespaces:483            return self.context.namespaces[key]484        ns = TemplateNamespace(485            uri,486            self.context._copy(),487            templateuri=uri,488            calling_uri=self._templateuri,489        )490        self.context.namespaces[key] = ns491        return ns492 493    def get_template(self, uri):494        """Return a :class:`.Template` from the given ``uri``.495 496        The ``uri`` resolution is relative to the ``uri`` of this497        :class:`.Namespace` object's :class:`.Template`.498 499        """500        return _lookup_template(self.context, uri, self._templateuri)501 502    def get_cached(self, key, **kwargs):503        """Return a value from the :class:`.Cache` referenced by this504        :class:`.Namespace` object's :class:`.Template`.505 506        The advantage to this method versus direct access to the507        :class:`.Cache` is that the configuration parameters508        declared in ``<%page>`` take effect here, thereby calling509        up the same configured backend as that configured510        by ``<%page>``.511 512        """513 514        return self.cache.get(key, **kwargs)515 516    @property517    def cache(self):518        """Return the :class:`.Cache` object referenced519        by this :class:`.Namespace` object's520        :class:`.Template`.521 522        """523        return self.template.cache524 525    def include_file(self, uri, **kwargs):526        """Include a file at the given ``uri``."""527 528        _include_file(self.context, uri, self._templateuri, **kwargs)529 530    def _populate(self, d, l):531        for ident in l:532            if ident == "*":533                for k, v in self._get_star():534                    d[k] = v535            else:536                d[ident] = getattr(self, ident)537 538    def _get_star(self):539        if self.callables:540            for key in self.callables:541                yield (key, self.callables[key])542 543    def __getattr__(self, key):544        if key in self.callables:545            val = self.callables[key]546        elif self.inherits:547            val = getattr(self.inherits, key)548        else:549            raise AttributeError(550                "Namespace '%s' has no member '%s'" % (self.name, key)551            )552        setattr(self, key, val)553        return val554 555 556class TemplateNamespace(Namespace):557 558    """A :class:`.Namespace` specific to a :class:`.Template` instance."""559 560    def __init__(561        self,562        name,563        context,564        template=None,565        templateuri=None,566        callables=None,567        inherits=None,568        populate_self=True,569        calling_uri=None,570    ):571        self.name = name572        self.context = context573        self.inherits = inherits574        if callables is not None:575            self.callables = {c.__name__: c for c in callables}576 577        if templateuri is not None:578            self.template = _lookup_template(context, templateuri, calling_uri)579            self._templateuri = self.template.module._template_uri580        elif template is not None:581            self.template = template582            self._templateuri = template.module._template_uri583        else:584            raise TypeError("'template' argument is required.")585 586        if populate_self:587            lclcallable, lclcontext = _populate_self_namespace(588                context, self.template, self_ns=self589            )590 591    @property592    def module(self):593        """The Python module referenced by this :class:`.Namespace`.594 595        If the namespace references a :class:`.Template`, then596        this module is the equivalent of ``template.module``,597        i.e. the generated module for the template.598 599        """600        return self.template.module601 602    @property603    def filename(self):604        """The path of the filesystem file used for this605        :class:`.Namespace`'s module or template.606        """607        return self.template.filename608 609    @property610    def uri(self):611        """The URI for this :class:`.Namespace`'s template.612 613        I.e. whatever was sent to :meth:`.TemplateLookup.get_template()`.614 615        This is the equivalent of :attr:`.Template.uri`.616 617        """618        return self.template.uri619 620    def _get_star(self):621        if self.callables:622            for key in self.callables:623                yield (key, self.callables[key])624 625        def get(key):626            callable_ = self.template._get_def_callable(key)627            return functools.partial(callable_, self.context)628 629        for k in self.template.module._exports:630            yield (k, get(k))631 632    def __getattr__(self, key):633        if key in self.callables:634            val = self.callables[key]635        elif self.template.has_def(key):636            callable_ = self.template._get_def_callable(key)637            val = functools.partial(callable_, self.context)638        elif self.inherits:639            val = getattr(self.inherits, key)640 641        else:642            raise AttributeError(643                "Namespace '%s' has no member '%s'" % (self.name, key)644            )645        setattr(self, key, val)646        return val647 648 649class ModuleNamespace(Namespace):650 651    """A :class:`.Namespace` specific to a Python module instance."""652 653    def __init__(654        self,655        name,656        context,657        module,658        callables=None,659        inherits=None,660        populate_self=True,661        calling_uri=None,662    ):663        self.name = name664        self.context = context665        self.inherits = inherits666        if callables is not None:667            self.callables = {c.__name__: c for c in callables}668 669        mod = __import__(module)670        for token in module.split(".")[1:]:671            mod = getattr(mod, token)672        self.module = mod673 674    @property675    def filename(self):676        """The path of the filesystem file used for this677        :class:`.Namespace`'s module or template.678        """679        return self.module.__file__680 681    def _get_star(self):682        if self.callables:683            for key in self.callables:684                yield (key, self.callables[key])685        for key in dir(self.module):686            if key[0] != "_":687                callable_ = getattr(self.module, key)688                if callable(callable_):689                    yield key, functools.partial(callable_, self.context)690 691    def __getattr__(self, key):692        if key in self.callables:693            val = self.callables[key]694        elif hasattr(self.module, key):695            callable_ = getattr(self.module, key)696            val = functools.partial(callable_, self.context)697        elif self.inherits:698            val = getattr(self.inherits, key)699        else:700            raise AttributeError(701                "Namespace '%s' has no member '%s'" % (self.name, key)702            )703        setattr(self, key, val)704        return val705 706 707def supports_caller(func):708    """Apply a caller_stack compatibility decorator to a plain709    Python function.710 711    See the example in :ref:`namespaces_python_modules`.712 713    """714 715    def wrap_stackframe(context, *args, **kwargs):716        context.caller_stack._push_frame()717        try:718            return func(context, *args, **kwargs)719        finally:720            context.caller_stack._pop_frame()721 722    return wrap_stackframe723 724 725def capture(context, callable_, *args, **kwargs):726    """Execute the given template def, capturing the output into727    a buffer.728 729    See the example in :ref:`namespaces_python_modules`.730 731    """732 733    if not callable(callable_):734        raise exceptions.RuntimeException(735            "capture() function expects a callable as "736            "its argument (i.e. capture(func, *args, **kwargs))"737        )738    context._push_buffer()739    try:740        callable_(*args, **kwargs)741    finally:742        buf = context._pop_buffer()743    return buf.getvalue()744 745 746def _decorate_toplevel(fn):747    def decorate_render(render_fn):748        def go(context, *args, **kw):749            def y(*args, **kw):750                return render_fn(context, *args, **kw)751 752            try:753                y.__name__ = render_fn.__name__[7:]754            except TypeError:755                # < Python 2.4756                pass757            return fn(y)(context, *args, **kw)758 759        return go760 761    return decorate_render762 763 764def _decorate_inline(context, fn):765    def decorate_render(render_fn):766        dec = fn(render_fn)767 768        def go(*args, **kw):769            return dec(context, *args, **kw)770 771        return go772 773    return decorate_render774 775 776def _include_file(context, uri, calling_uri, **kwargs):777    """locate the template from the given uri and include it in778    the current output."""779 780    template = _lookup_template(context, uri, calling_uri)781    (callable_, ctx) = _populate_self_namespace(782        context._clean_inheritance_tokens(), template783    )784    kwargs = _kwargs_for_include(callable_, context._data, **kwargs)785    if template.include_error_handler:786        try:787            callable_(ctx, **kwargs)788        except Exception:789            result = template.include_error_handler(ctx, compat.exception_as())790            if not result:791                raise792    else:793        callable_(ctx, **kwargs)794 795 796def _inherit_from(context, uri, calling_uri):797    """called by the _inherit method in template modules to set798    up the inheritance chain at the start of a template's799    execution."""800 801    if uri is None:802        return None803    template = _lookup_template(context, uri, calling_uri)804    self_ns = context["self"]805    ih = self_ns806    while ih.inherits is not None:807        ih = ih.inherits808    lclcontext = context._locals({"next": ih})809    ih.inherits = TemplateNamespace(810        "self:%s" % template.uri,811        lclcontext,812        template=template,813        populate_self=False,814    )815    context._data["parent"] = lclcontext._data["local"] = ih.inherits816    callable_ = getattr(template.module, "_mako_inherit", None)817    if callable_ is not None:818        ret = callable_(template, lclcontext)819        if ret:820            return ret821 822    gen_ns = getattr(template.module, "_mako_generate_namespaces", None)823    if gen_ns is not None:824        gen_ns(context)825    return (template.callable_, lclcontext)826 827 828def _lookup_template(context, uri, relativeto):829    lookup = context._with_template.lookup830    if lookup is None:831        raise exceptions.TemplateLookupException(832            "Template '%s' has no TemplateLookup associated"833            % context._with_template.uri834        )835    uri = lookup.adjust_uri(uri, relativeto)836    try:837        return lookup.get_template(uri)838    except exceptions.TopLevelLookupException as e:839        raise exceptions.TemplateLookupException(840            str(compat.exception_as())841        ) from e842 843 844def _populate_self_namespace(context, template, self_ns=None):845    if self_ns is None:846        self_ns = TemplateNamespace(847            "self:%s" % template.uri,848            context,849            template=template,850            populate_self=False,851        )852    context._data["self"] = context._data["local"] = self_ns853    if hasattr(template.module, "_mako_inherit"):854        ret = template.module._mako_inherit(template, context)855        if ret:856            return ret857    return (template.callable_, context)858 859 860def _render(template, callable_, args, data, as_unicode=False):861    """create a Context and return the string862    output of the given template and template callable."""863 864    if as_unicode:865        buf = util.FastEncodingBuffer()866    else:867        buf = util.FastEncodingBuffer(868            encoding=template.output_encoding, errors=template.encoding_errors869        )870    context = Context(buf, **data)871    context._outputting_as_unicode = as_unicode872    context._set_with_template(template)873 874    _render_context(875        template,876        callable_,877        context,878        *args,879        **_kwargs_for_callable(callable_, data),880    )881    return context._pop_buffer().getvalue()882 883 884def _kwargs_for_callable(callable_, data):885    argspec = compat.inspect_getargspec(callable_)886    # for normal pages, **pageargs is usually present887    if argspec[2]:888        return data889 890    # for rendering defs from the top level, figure out the args891    namedargs = argspec[0] + [v for v in argspec[1:3] if v is not None]892    kwargs = {}893    for arg in namedargs:894        if arg != "context" and arg in data and arg not in kwargs:895            kwargs[arg] = data[arg]896    return kwargs897 898 899def _kwargs_for_include(callable_, data, **kwargs):900    argspec = compat.inspect_getargspec(callable_)901    namedargs = argspec[0] + [v for v in argspec[1:3] if v is not None]902    for arg in namedargs:903        if arg != "context" and arg in data and arg not in kwargs:904            kwargs[arg] = data[arg]905    return kwargs906 907 908def _render_context(tmpl, callable_, context, *args, **kwargs):909    import mako.template as template910 911    # create polymorphic 'self' namespace for this912    # template with possibly updated context913    if not isinstance(tmpl, template.DefTemplate):914        # if main render method, call from the base of the inheritance stack915        (inherit, lclcontext) = _populate_self_namespace(context, tmpl)916        _exec_template(inherit, lclcontext, args=args, kwargs=kwargs)917    else:918        # otherwise, call the actual rendering method specified919        (inherit, lclcontext) = _populate_self_namespace(context, tmpl.parent)920        _exec_template(callable_, context, args=args, kwargs=kwargs)921 922 923def _exec_template(callable_, context, args=None, kwargs=None):924    """execute a rendering callable given the callable, a925    Context, and optional explicit arguments926 927    the contextual Template will be located if it exists, and928    the error handling options specified on that Template will929    be interpreted here.930    """931    template = context._with_template932    if template is not None and (933        template.format_exceptions or template.error_handler934    ):935        try:936            callable_(context, *args, **kwargs)937        except Exception:938            _render_error(template, context, compat.exception_as())939        except:940            e = sys.exc_info()[0]941            _render_error(template, context, e)942    else:943        callable_(context, *args, **kwargs)944 945 946def _render_error(template, context, error):947    if template.error_handler:948        result = template.error_handler(context, error)949        if not result:950            tp, value, tb = sys.exc_info()951            if value and tb:952                raise value.with_traceback(tb)953            else:954                raise error955    else:956        error_template = exceptions.html_error_template()957        if context._outputting_as_unicode:958            context._buffer_stack[:] = [util.FastEncodingBuffer()]959        else:960            context._buffer_stack[:] = [961                util.FastEncodingBuffer(962                    error_template.output_encoding,963                    error_template.encoding_errors,964                )965            ]966 967        context._set_with_template(error_template)968        error_template.render_context(context, error=error)969 
codekingpro/portable-devtools · Team Ai