Team Ai
Datasetpublic

codekingpro/portable-devtools

sourceHugging Faceupdated 5mo agoView on Hugging Face
1likes14kdownloads
parsetree.py657 linesDownload Raw Back to mako
1# mako/parsetree.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 7"""defines the parse tree components for Mako templates."""8 9import re10 11from mako import ast12from mako import exceptions13from mako import filters14from mako import util15 16 17class Node:18 19    """base class for a Node in the parse tree."""20 21    def __init__(self, source, lineno, pos, filename):22        self.source = source23        self.lineno = lineno24        self.pos = pos25        self.filename = filename26 27    @property28    def exception_kwargs(self):29        return {30            "source": self.source,31            "lineno": self.lineno,32            "pos": self.pos,33            "filename": self.filename,34        }35 36    def get_children(self):37        return []38 39    def accept_visitor(self, visitor):40        def traverse(node):41            for n in node.get_children():42                n.accept_visitor(visitor)43 44        method = getattr(visitor, "visit" + self.__class__.__name__, traverse)45        method(self)46 47 48class TemplateNode(Node):49 50    """a 'container' node that stores the overall collection of nodes."""51 52    def __init__(self, filename):53        super().__init__("", 0, 0, filename)54        self.nodes = []55        self.page_attributes = {}56 57    def get_children(self):58        return self.nodes59 60    def __repr__(self):61        return "TemplateNode(%s, %r)" % (62            util.sorted_dict_repr(self.page_attributes),63            self.nodes,64        )65 66 67class ControlLine(Node):68 69    """defines a control line, a line-oriented python line or end tag.70 71    e.g.::72 73        % if foo:74            (markup)75        % endif76 77    """78 79    has_loop_context = False80 81    def __init__(self, keyword, isend, text, **kwargs):82        super().__init__(**kwargs)83        self.text = text84        self.keyword = keyword85        self.isend = isend86        self.is_primary = keyword in ["for", "if", "while", "try", "with"]87        self.nodes = []88        if self.isend:89            self._declared_identifiers = []90            self._undeclared_identifiers = []91        else:92            code = ast.PythonFragment(text, **self.exception_kwargs)93            self._declared_identifiers = code.declared_identifiers94            self._undeclared_identifiers = code.undeclared_identifiers95 96    def get_children(self):97        return self.nodes98 99    def declared_identifiers(self):100        return self._declared_identifiers101 102    def undeclared_identifiers(self):103        return self._undeclared_identifiers104 105    def is_ternary(self, keyword):106        """return true if the given keyword is a ternary keyword107        for this ControlLine"""108 109        cases = {110            "if": {"else", "elif"},111            "try": {"except", "finally"},112            "for": {"else"},113        }114 115        return keyword in cases.get(self.keyword, set())116 117    def __repr__(self):118        return "ControlLine(%r, %r, %r, %r)" % (119            self.keyword,120            self.text,121            self.isend,122            (self.lineno, self.pos),123        )124 125 126class Text(Node):127    """defines plain text in the template."""128 129    def __init__(self, content, **kwargs):130        super().__init__(**kwargs)131        self.content = content132 133    def __repr__(self):134        return "Text(%r, %r)" % (self.content, (self.lineno, self.pos))135 136 137class Code(Node):138    """defines a Python code block, either inline or module level.139 140    e.g.::141 142        inline:143        <%144            x = 12145        %>146 147        module level:148        <%!149            import logger150        %>151 152    """153 154    def __init__(self, text, ismodule, **kwargs):155        super().__init__(**kwargs)156        self.text = text157        self.ismodule = ismodule158        self.code = ast.PythonCode(text, **self.exception_kwargs)159 160    def declared_identifiers(self):161        return self.code.declared_identifiers162 163    def undeclared_identifiers(self):164        return self.code.undeclared_identifiers165 166    def __repr__(self):167        return "Code(%r, %r, %r)" % (168            self.text,169            self.ismodule,170            (self.lineno, self.pos),171        )172 173 174class Comment(Node):175    """defines a comment line.176 177    # this is a comment178 179    """180 181    def __init__(self, text, **kwargs):182        super().__init__(**kwargs)183        self.text = text184 185    def __repr__(self):186        return "Comment(%r, %r)" % (self.text, (self.lineno, self.pos))187 188 189class Expression(Node):190    """defines an inline expression.191 192    ${x+y}193 194    """195 196    def __init__(self, text, escapes, **kwargs):197        super().__init__(**kwargs)198        self.text = text199        self.escapes = escapes200        self.escapes_code = ast.ArgumentList(escapes, **self.exception_kwargs)201        self.code = ast.PythonCode(text, **self.exception_kwargs)202 203    def declared_identifiers(self):204        return []205 206    def undeclared_identifiers(self):207        # TODO: make the "filter" shortcut list configurable at parse/gen time208        return self.code.undeclared_identifiers.union(209            self.escapes_code.undeclared_identifiers.difference(210                filters.DEFAULT_ESCAPES211            )212        ).difference(self.code.declared_identifiers)213 214    def __repr__(self):215        return "Expression(%r, %r, %r)" % (216            self.text,217            self.escapes_code.args,218            (self.lineno, self.pos),219        )220 221 222class _TagMeta(type):223    """metaclass to allow Tag to produce a subclass according to224    its keyword"""225 226    _classmap = {}227 228    def __init__(cls, clsname, bases, dict_):229        if getattr(cls, "__keyword__", None) is not None:230            cls._classmap[cls.__keyword__] = cls231        super().__init__(clsname, bases, dict_)232 233    def __call__(cls, keyword, attributes, **kwargs):234        if ":" in keyword:235            ns, defname = keyword.split(":")236            return type.__call__(237                CallNamespaceTag, ns, defname, attributes, **kwargs238            )239 240        try:241            cls = _TagMeta._classmap[keyword]242        except KeyError:243            raise exceptions.CompileException(244                "No such tag: '%s'" % keyword,245                source=kwargs["source"],246                lineno=kwargs["lineno"],247                pos=kwargs["pos"],248                filename=kwargs["filename"],249            )250        return type.__call__(cls, keyword, attributes, **kwargs)251 252 253class Tag(Node, metaclass=_TagMeta):254    """abstract base class for tags.255 256    e.g.::257 258        <%sometag/>259 260        <%someothertag>261            stuff262        </%someothertag>263 264    """265 266    __keyword__ = None267 268    def __init__(269        self,270        keyword,271        attributes,272        expressions,273        nonexpressions,274        required,275        **kwargs,276    ):277        r"""construct a new Tag instance.278 279        this constructor not called directly, and is only called280        by subclasses.281 282        :param keyword: the tag keyword283 284        :param attributes: raw dictionary of attribute key/value pairs285 286        :param expressions: a set of identifiers that are legal attributes,287         which can also contain embedded expressions288 289        :param nonexpressions: a set of identifiers that are legal290         attributes, which cannot contain embedded expressions291 292        :param \**kwargs:293         other arguments passed to the Node superclass (lineno, pos)294 295        """296        super().__init__(**kwargs)297        self.keyword = keyword298        self.attributes = attributes299        self._parse_attributes(expressions, nonexpressions)300        missing = [r for r in required if r not in self.parsed_attributes]301        if len(missing):302            raise exceptions.CompileException(303                (304                    "Missing attribute(s): %s"305                    % ",".join(repr(m) for m in missing)306                ),307                **self.exception_kwargs,308            )309 310        self.parent = None311        self.nodes = []312 313    def is_root(self):314        return self.parent is None315 316    def get_children(self):317        return self.nodes318 319    def _parse_attributes(self, expressions, nonexpressions):320        undeclared_identifiers = set()321        self.parsed_attributes = {}322        for key in self.attributes:323            if key in expressions:324                expr = []325                for x in re.compile(r"(\${.+?})", re.S).split(326                    self.attributes[key]327                ):328                    m = re.compile(r"^\${(.+?)}$", re.S).match(x)329                    if m:330                        code = ast.PythonCode(331                            m.group(1).rstrip(), **self.exception_kwargs332                        )333                        # we aren't discarding "declared_identifiers" here,334                        # which we do so that list comprehension-declared335                        # variables aren't counted.   As yet can't find a336                        # condition that requires it here.337                        undeclared_identifiers = undeclared_identifiers.union(338                            code.undeclared_identifiers339                        )340                        expr.append("(%s)" % m.group(1))341                    elif x:342                        expr.append(repr(x))343                self.parsed_attributes[key] = " + ".join(expr) or repr("")344            elif key in nonexpressions:345                if re.search(r"\${.+?}", self.attributes[key]):346                    raise exceptions.CompileException(347                        "Attribute '%s' in tag '%s' does not allow embedded "348                        "expressions" % (key, self.keyword),349                        **self.exception_kwargs,350                    )351                self.parsed_attributes[key] = repr(self.attributes[key])352            else:353                raise exceptions.CompileException(354                    "Invalid attribute for tag '%s': '%s'"355                    % (self.keyword, key),356                    **self.exception_kwargs,357                )358        self.expression_undeclared_identifiers = undeclared_identifiers359 360    def declared_identifiers(self):361        return []362 363    def undeclared_identifiers(self):364        return self.expression_undeclared_identifiers365 366    def __repr__(self):367        return "%s(%r, %s, %r, %r)" % (368            self.__class__.__name__,369            self.keyword,370            util.sorted_dict_repr(self.attributes),371            (self.lineno, self.pos),372            self.nodes,373        )374 375 376class IncludeTag(Tag):377    __keyword__ = "include"378 379    def __init__(self, keyword, attributes, **kwargs):380        super().__init__(381            keyword,382            attributes,383            ("file", "import", "args"),384            (),385            ("file",),386            **kwargs,387        )388        self.page_args = ast.PythonCode(389            "__DUMMY(%s)" % attributes.get("args", ""), **self.exception_kwargs390        )391 392    def declared_identifiers(self):393        return []394 395    def undeclared_identifiers(self):396        identifiers = self.page_args.undeclared_identifiers.difference(397            {"__DUMMY"}398        ).difference(self.page_args.declared_identifiers)399        return identifiers.union(super().undeclared_identifiers())400 401 402class NamespaceTag(Tag):403    __keyword__ = "namespace"404 405    def __init__(self, keyword, attributes, **kwargs):406        super().__init__(407            keyword,408            attributes,409            ("file",),410            ("name", "inheritable", "import", "module"),411            (),412            **kwargs,413        )414 415        self.name = attributes.get("name", "__anon_%s" % hex(abs(id(self))))416        if "name" not in attributes and "import" not in attributes:417            raise exceptions.CompileException(418                "'name' and/or 'import' attributes are required "419                "for <%namespace>",420                **self.exception_kwargs,421            )422        if "file" in attributes and "module" in attributes:423            raise exceptions.CompileException(424                "<%namespace> may only have one of 'file' or 'module'",425                **self.exception_kwargs,426            )427 428    def declared_identifiers(self):429        return []430 431 432class TextTag(Tag):433    __keyword__ = "text"434 435    def __init__(self, keyword, attributes, **kwargs):436        super().__init__(keyword, attributes, (), ("filter"), (), **kwargs)437        self.filter_args = ast.ArgumentList(438            attributes.get("filter", ""), **self.exception_kwargs439        )440 441    def undeclared_identifiers(self):442        return self.filter_args.undeclared_identifiers.difference(443            filters.DEFAULT_ESCAPES.keys()444        ).union(self.expression_undeclared_identifiers)445 446 447class DefTag(Tag):448    __keyword__ = "def"449 450    def __init__(self, keyword, attributes, **kwargs):451        expressions = ["buffered", "cached"] + [452            c for c in attributes if c.startswith("cache_")453        ]454 455        super().__init__(456            keyword,457            attributes,458            expressions,459            ("name", "filter", "decorator"),460            ("name",),461            **kwargs,462        )463        name = attributes["name"]464        if re.match(r"^[\w_]+$", name):465            raise exceptions.CompileException(466                "Missing parenthesis in %def", **self.exception_kwargs467            )468        self.function_decl = ast.FunctionDecl(469            "def " + name + ":pass", **self.exception_kwargs470        )471        self.name = self.function_decl.funcname472        self.decorator = attributes.get("decorator", "")473        self.filter_args = ast.ArgumentList(474            attributes.get("filter", ""), **self.exception_kwargs475        )476 477    is_anonymous = False478    is_block = False479 480    @property481    def funcname(self):482        return self.function_decl.funcname483 484    def get_argument_expressions(self, **kw):485        return self.function_decl.get_argument_expressions(**kw)486 487    def declared_identifiers(self):488        return self.function_decl.allargnames489 490    def undeclared_identifiers(self):491        res = []492        for c in self.function_decl.defaults:493            res += list(494                ast.PythonCode(495                    c, **self.exception_kwargs496                ).undeclared_identifiers497            )498        return (499            set(res)500            .union(501                self.filter_args.undeclared_identifiers.difference(502                    filters.DEFAULT_ESCAPES.keys()503                )504            )505            .union(self.expression_undeclared_identifiers)506            .difference(self.function_decl.allargnames)507        )508 509 510class BlockTag(Tag):511    __keyword__ = "block"512 513    def __init__(self, keyword, attributes, **kwargs):514        expressions = ["buffered", "cached", "args"] + [515            c for c in attributes if c.startswith("cache_")516        ]517 518        super().__init__(519            keyword,520            attributes,521            expressions,522            ("name", "filter", "decorator"),523            (),524            **kwargs,525        )526        name = attributes.get("name")527        if name and not re.match(r"^[\w_]+$", name):528            raise exceptions.CompileException(529                "%block may not specify an argument signature",530                **self.exception_kwargs,531            )532        if not name and attributes.get("args", None):533            raise exceptions.CompileException(534                "Only named %blocks may specify args", **self.exception_kwargs535            )536        self.body_decl = ast.FunctionArgs(537            attributes.get("args", ""), **self.exception_kwargs538        )539 540        self.name = name541        self.decorator = attributes.get("decorator", "")542        self.filter_args = ast.ArgumentList(543            attributes.get("filter", ""), **self.exception_kwargs544        )545 546    is_block = True547 548    @property549    def is_anonymous(self):550        return self.name is None551 552    @property553    def funcname(self):554        return self.name or "__M_anon_%d" % (self.lineno,)555 556    def get_argument_expressions(self, **kw):557        return self.body_decl.get_argument_expressions(**kw)558 559    def declared_identifiers(self):560        return self.body_decl.allargnames561 562    def undeclared_identifiers(self):563        return (564            self.filter_args.undeclared_identifiers.difference(565                filters.DEFAULT_ESCAPES.keys()566            )567        ).union(self.expression_undeclared_identifiers)568 569 570class CallTag(Tag):571    __keyword__ = "call"572 573    def __init__(self, keyword, attributes, **kwargs):574        super().__init__(575            keyword, attributes, ("args"), ("expr",), ("expr",), **kwargs576        )577        self.expression = attributes["expr"]578        self.code = ast.PythonCode(self.expression, **self.exception_kwargs)579        self.body_decl = ast.FunctionArgs(580            attributes.get("args", ""), **self.exception_kwargs581        )582 583    def declared_identifiers(self):584        return self.code.declared_identifiers.union(self.body_decl.allargnames)585 586    def undeclared_identifiers(self):587        return self.code.undeclared_identifiers.difference(588            self.code.declared_identifiers589        )590 591 592class CallNamespaceTag(Tag):593    def __init__(self, namespace, defname, attributes, **kwargs):594        super().__init__(595            namespace + ":" + defname,596            attributes,597            tuple(attributes.keys()) + ("args",),598            (),599            (),600            **kwargs,601        )602 603        self.expression = "%s.%s(%s)" % (604            namespace,605            defname,606            ",".join(607                "%s=%s" % (k, v)608                for k, v in self.parsed_attributes.items()609                if k != "args"610            ),611        )612 613        self.code = ast.PythonCode(self.expression, **self.exception_kwargs)614        self.body_decl = ast.FunctionArgs(615            attributes.get("args", ""), **self.exception_kwargs616        )617 618    def declared_identifiers(self):619        return self.code.declared_identifiers.union(self.body_decl.allargnames)620 621    def undeclared_identifiers(self):622        return self.code.undeclared_identifiers.difference(623            self.code.declared_identifiers624        )625 626 627class InheritTag(Tag):628    __keyword__ = "inherit"629 630    def __init__(self, keyword, attributes, **kwargs):631        super().__init__(632            keyword, attributes, ("file",), (), ("file",), **kwargs633        )634 635 636class PageTag(Tag):637    __keyword__ = "page"638 639    def __init__(self, keyword, attributes, **kwargs):640        expressions = [641            "cached",642            "args",643            "expression_filter",644            "enable_loop",645        ] + [c for c in attributes if c.startswith("cache_")]646 647        super().__init__(keyword, attributes, expressions, (), (), **kwargs)648        self.body_decl = ast.FunctionArgs(649            attributes.get("args", ""), **self.exception_kwargs650        )651        self.filter_args = ast.ArgumentList(652            attributes.get("expression_filter", ""), **self.exception_kwargs653        )654 655    def declared_identifiers(self):656        return self.body_decl.allargnames657 
codekingpro/portable-devtools · Team Ai