Exemplo n.º 1
0
    def visit_element(self, start, end, children):
        if self.translatestack and self.translatestack[-1]:
            self.translatestack[-1].add_element(start)

        attributes = start['ns_attrs']
        plain_attrs = dict(
            (a['name'].split(':')[-1], a['value']) for a in start['attrs'])
        new_domain = attributes.get((I18N_NS, 'domain'))
        if new_domain:
            self.domainstack.append(new_domain)
        elif self.domainstack:
            self.domainstack.append(self.domainstack[-1])

        i18n_translate = attributes.get((I18N_NS, 'translate'))
        if i18n_translate is not None:
            self.translatestack.append(
                TranslateContext(
                    self.domainstack[-1] if self.domainstack else None,
                    i18n_translate, self.filename, self.linenumber))
        else:
            self.translatestack.append(None)

        if self.domainstack:
            i18n_attributes = attributes.get((I18N_NS, 'attributes'))
            if i18n_attributes:
                parts = [p.strip() for p in i18n_attributes.split(';')]
                for msgid in parts:
                    if ' ' not in msgid:
                        if msgid not in plain_attrs:
                            continue
                        self.add_message(plain_attrs[msgid])
                    else:
                        try:
                            (attr, msgid) = msgid.split()
                        except ValueError:
                            continue
                        if attr not in plain_attrs:
                            continue
                        self.add_message(msgid,
                                         u'Default: %s' % plain_attrs[attr])

            for (attribute, value) in attributes.items():
                value = decode_htmlentities(value)
                for source in self.get_code_for_attribute(attribute, value):
                    self.parse_python(source)

        for child in children:
            self.visit(*child)

        if self.domainstack:
            self.domainstack.pop()

        translate = self.translatestack.pop()
        if translate and not translate.ignore() and translate.domain and \
                (self.target_domain is None or translate.domain == self.target_domain):
            self.messages.append(translate.message())
Exemplo n.º 2
0
Arquivo: xml.py Projeto: voipro/lingua
    def visit_element(self, start, end, children):
        if self.translatestack and self.translatestack[-1]:
            self.translatestack[-1].add_element(start)

        attributes = start['ns_attrs']
        plain_attrs = dict((a['name'].split(':')[-1], a['value']) for a in start['attrs'])
        new_domain = attributes.get((I18N_NS, 'domain'))
        if new_domain:
            self.domainstack.append(new_domain)
        elif self.domainstack:
            self.domainstack.append(self.domainstack[-1])

        i18n_translate = attributes.get((I18N_NS, 'translate'))
        if i18n_translate is not None:
            self.translatestack.append(TranslateContext(
                self.domainstack[-1] if self.domainstack else None,
                i18n_translate, self.filename, self.linenumber))
        else:
            self.translatestack.append(None)

        if self.domainstack:
            i18n_attributes = attributes.get((I18N_NS, 'attributes'))
            if i18n_attributes:
                parts = [p.strip() for p in i18n_attributes.split(';')]
                for msgid in parts:
                    if ' ' not in msgid:
                        if msgid not in plain_attrs:
                            continue
                        self.add_message(plain_attrs[msgid])
                    else:
                        try:
                            (attr, msgid) = msgid.split()
                        except ValueError:
                            continue
                        if attr not in plain_attrs:
                            continue
                        self.add_message(msgid, u'Default: %s' % plain_attrs[attr])

            for (attribute, value) in attributes.items():
                value = decode_htmlentities(value)
                for source in self.get_code_for_attribute(attribute, value):
                    self.parse_python(source)

        for child in children:
            self.visit(*child)

        if self.domainstack:
            self.domainstack.pop()

        translate = self.translatestack.pop()
        if translate and not translate.ignore() and translate.domain and \
                (self.target_domain is None or translate.domain == self.target_domain):
            self.messages.append(translate.message())
Exemplo n.º 3
0
 def visit_text(self, data):
     if self.target_domain is None or self.target_domain == self.domainstack[-1][0]:
         default_engine = self.config["default-engine"]
         for line in data.splitlines():
             line = decode_htmlentities(line)
             try:
                 for source in get_python_expressions(line, default_engine):
                     if UNDERSCORE_CALL.search(source):
                         self.parse_python(source)
             except SyntaxError:
                 print("Aborting due to Python syntax error in %s[%d]: %s" % (self.filename, self.linenumber, line))
                 sys.exit(1)
         if self.translatestack[-1]:
             self.translatestack[-1].add_text(data)
     self.linenumber += get_newline_count(data)
Exemplo n.º 4
0
 def visit_text(self, data):
     if self.target_domain is None or self.target_domain == self.domainstack[
             -1][0]:
         default_engine = self.config['default-engine']
         for line in data.splitlines():
             line = decode_htmlentities(line)
             try:
                 for source in get_python_expressions(line, default_engine):
                     if UNDERSCORE_CALL.search(source):
                         self.parse_python(source)
             except SyntaxError:
                 print('Aborting due to Python syntax error in %s[%d]: %s' %
                       (self.filename, self.linenumber, line))
                 sys.exit(1)
         if self.translatestack[-1]:
             self.translatestack[-1].add_text(data)
     self.linenumber += get_newline_count(data)
Exemplo n.º 5
0
    def visit_element(self, start, end, children):
        self.linenumber += get_newline_count(start["prefix"] + start["name"])
        if self.translatestack and self.translatestack[-1]:
            self.translatestack[-1].add_element(start)

        attributes = start["ns_attrs"]
        plain_attrs = get_plain_attrs(start["attrs"])
        childs_lineno = self.linenumber
        post_offset = [x[2] for x in plain_attrs.values()]
        if post_offset:
            childs_lineno += max(post_offset)
        childs_lineno += get_newline_count(start["suffix"])
        new_domain = attributes.get((I18N_NS, "domain"))
        old_domain = self.domainstack[-1][0] if self.domainstack else None
        new_context = attributes.get((I18N_NS, "context"))
        old_context = self.domainstack[-1][1] if self.domainstack else None
        new_comment = attributes.get((I18N_NS, "comment"))
        old_comment = self.domainstack[-1][2] if self.domainstack else None
        if new_domain or new_context or new_comment:
            self.domainstack.append((new_domain or old_domain, new_context or old_context, new_comment or old_comment))
        elif self.domainstack:
            self.domainstack.append(self.domainstack[-1])

        current_domain = self.domainstack[-1][0]
        include_domain = self.target_domain is None or self.target_domain == current_domain

        i18n_translate = attributes.get((I18N_NS, "translate"))
        if i18n_translate is not None:
            ctx = TranslateContext(
                self.domainstack[-1][0] if self.domainstack else None,
                self.domainstack[-1][1] if self.domainstack else None,
                i18n_translate,
                self.domainstack[-1][2] if self.domainstack else None,
                self.filename,
                childs_lineno,
            )
            if self.translatestack:
                ctx.parent = self.translatestack[-1]
                if ctx.parent is not None:
                    ctx.parent.register_child(start, ctx)
            self.translatestack.append(ctx)
        else:
            self.translatestack.append(None)

        if self.domainstack:
            i18n_attributes = attributes.get((I18N_NS, "attributes"))
            if i18n_attributes and include_domain:
                parts = [p.strip() for p in i18n_attributes.split(";")]
                for msgid in parts:
                    if " " not in msgid:
                        if msgid not in plain_attrs:
                            continue
                        value, offset, post_offset = plain_attrs[msgid]
                        self.add_message(value, self.domainstack[-1][2] or "", offset=offset)
                    else:
                        try:
                            (attr, msgid) = msgid.split()
                        except ValueError:
                            continue
                        if attr not in plain_attrs:
                            continue
                        value, offset, post_offset = plain_attrs[attr]
                        self.add_message(msgid, u"Default: %s" % value, offset=offset)

            for (attribute, value) in attributes.items():
                value = decode_htmlentities(value)
                for source in self.get_code_for_attribute(attribute, value):
                    self.parse_python(source)

        self.linenumber = childs_lineno
        for child in children:
            self.visit(*child)

        if end is not None:
            self.linenumber += get_newline_count(end["prefix"] + end["name"])
            post_offset = [x[2] for x in get_plain_attrs(end["attrs"]).values()]
            if post_offset:
                self.linenumber += max(post_offset)
            self.linenumber += get_newline_count(end["suffix"])

        if self.domainstack:
            self.domainstack.pop()

        translate = self.translatestack.pop()
        if translate and not translate.ignore() and translate.domain and include_domain:
            self.messages.append(translate)
Exemplo n.º 6
0
    def prev_visit_element(self, start, end, children):
        ns = start['ns_attrs']

        for (prefix, attr), encoded in tuple(ns.items()):
            if prefix == TAL:
                ns[prefix, attr] = decode_htmlentities(encoded)

        # Validate namespace attributes
        validate_attributes(ns, TAL, tal.WHITELIST)
        validate_attributes(ns, I18N, i18n.WHITELIST)

        # Check attributes for language errors
        self._check_attributes(start['namespace'], ns)

        # Remember whitespace for item repetition
        if self._last is not None:
            self._whitespace = "\n" + " " * len(self._last.rsplit('\n', 1)[-1])

        # Set element-local whitespace
        whitespace = self._whitespace

        # Set up switch
        try:
            clause = ns[TAL, 'switch']
        except KeyError:
            switch = None
        else:
            switch = nodes.Value(clause)

        self._switches.append(switch)

        body = []

        content = nodes.Sequence(body)

        # tal:content
        try:
            clause = ns[TAL, 'content']
        except KeyError:
            pass
        else:
            key, value = tal.parse_substitution(clause)
            xlate = True if ns.get((I18N, 'translate')) == '' else False
            content = self._make_content_node(value, content, key, xlate)

            if end is None:
                # Make sure start-tag has opening suffix.
                start['suffix']  = ">"

                # Explicitly set end-tag.
                end = {
                    'prefix': '</',
                    'name': start['name'],
                    'space': '',
                    'suffix': '>'
                }

        # i18n:translate
        try:
            clause = ns[I18N, 'translate']
        except KeyError:
            pass
        else:
            dynamic = ns.get((TAL, 'content')) or ns.get((TAL, 'replace'))

            if not dynamic:
                content = nodes.Translate(clause, content)

        # tal:attributes
        try:
            clause = ns[TAL, 'attributes']
        except KeyError:
            TAL_ATTRIBUTES = {}
        else:
            TAL_ATTRIBUTES = tal.parse_attributes(clause)

        # i18n:attributes
        try:
            clause = ns[I18N, 'attributes']
        except KeyError:
            I18N_ATTRIBUTES = {}
        else:
            I18N_ATTRIBUTES = i18n.parse_attributes(clause)

        # Prepare attributes from TAL language
        prepared = tal.prepare_attributes(
            start['attrs'], TAL_ATTRIBUTES,
            I18N_ATTRIBUTES, ns, self.DROP_NS
        )

        # Create attribute nodes
        STATIC_ATTRIBUTES = self._create_static_attributes(prepared)
        ATTRIBUTES = self._create_attributes_nodes(
            prepared, I18N_ATTRIBUTES
        )

        # Start- and end nodes
        start_tag = nodes.Start(
            start['name'],
            self._maybe_trim(start['prefix']),
            self._maybe_trim(start['suffix']),
            ATTRIBUTES
        )
        stag = start_tag

        end_tag = nodes.End(
            end['name'],
            end['space'],
            self._maybe_trim(end['prefix']),
            self._maybe_trim(end['suffix']),
        ) if end is not None else None

        # tal:omit-tag
        try:
            clause = ns[TAL, 'omit-tag']
        except KeyError:
            omit = False
        else:
            clause = clause.strip()

            if clause == "":
                omit = True
            else:
                expression = nodes.Negate(nodes.Value(clause))
                omit = expression

                # Wrap start- and end-tags in condition
                start_tag = nodes.Condition(expression, start_tag)

                if end_tag is not None:
                    end_tag = nodes.Condition(expression, end_tag)

        if omit is True or start['namespace'] in self.DROP_NS:
            inner = content
        else:
            inner = nodes.Element(
                start_tag,
                end_tag,
                content,
            )

            # Assign static attributes dictionary to "attrs" value
            inner = nodes.Define(
                [nodes.Alias(["attrs"], STATIC_ATTRIBUTES)],
                inner,
            )

            if omit is not False:
                inner = nodes.Cache([omit], inner)

        # tal:replace
        try:
            clause = ns[TAL, 'replace']
        except KeyError:
            pass
        else:
            key, value = tal.parse_substitution(clause)
            xlate = True if ns.get((I18N, 'translate')) == '' else False
            inner = self._make_content_node(value, inner, key, xlate)

        # tal:define
        try:
            clause = ns[TAL, 'define']
        except KeyError:
            DEFINE = skip
        else:
            defines = tal.parse_defines(clause)
            if defines is None:
                raise ParseError("Invalid define syntax.", clause)

            DEFINE = partial(
                nodes.Define,
                [nodes.Assignment(
                    names, nodes.Value(expr), context == "local")
                 for (context, names, expr) in defines],
            )

        # tal:case
        try:
            clause = ns[TAL, 'case']
        except KeyError:
            CASE = skip
        else:
            value = nodes.Value(clause)
            for switch in reversed(self._switches):
                if switch is not None:
                    break
            else:
                raise LanguageError(
                    "Must define switch on a parent element.", clause
                )

            CASE = lambda node: nodes.Define(
                [nodes.Assignment(["default"], switch, True)],
                nodes.Condition(
                    nodes.Equality(switch, value),
                    node,
                )
            )

        # tal:repeat
        try:
            clause = ns[TAL, 'repeat']
        except KeyError:
            REPEAT = skip
        else:
            defines = tal.parse_defines(clause)
            assert len(defines) == 1
            context, names, expr = defines[0]

            expression = nodes.Value(expr)

            REPEAT = partial(
                nodes.Repeat,
                names,
                expression,
                context == "local",
                whitespace
            )

        # tal:condition
        try:
            clause = ns[TAL, 'condition']
        except KeyError:
            CONDITION = skip
        else:
            expression = nodes.Value(clause)
            CONDITION = partial(nodes.Condition, expression)

        # tal:switch
        if switch is None:
            SWITCH = skip
        else:
            SWITCH = partial(nodes.Cache, [switch])

        # i18n:domain
        try:
            clause = ns[I18N, 'domain']
        except KeyError:
            DOMAIN = skip
        else:
            DOMAIN = partial(nodes.Domain, clause)

        # i18n:name
        try:
            clause = ns[I18N, 'name']
        except KeyError:
            NAME = skip
        else:
            NAME = partial(nodes.Name, clause)

        # The "slot" node next is the first node level that can serve
        # as a macro slot
        slot = wrap(
            inner,
            DEFINE,
            CASE,
            CONDITION,
            REPEAT,
            SWITCH,
            DOMAIN,
        )

        slot = wrap(
            slot,
            NAME
        )

        # tal:on-error
        try:
            clause = ns[TAL, 'on-error']
        except KeyError:
            ON_ERROR = skip
        else:
            key, value = tal.parse_substitution(clause)
            translate = True if ns.get((I18N, 'translate')) == '' else False

            fallback = self._make_content_node(value, None, key, translate)

            if omit is False and start['namespace'] not in self.DROP_NS:
                fallback = nodes.Element(
                    start_tag,
                    end_tag,
                    fallback,
                )

            ON_ERROR = partial(nodes.OnError, fallback, 'error')

        clause = ns.get((META, 'interpolation'))
        if clause in ('false', 'off'):
            INTERPOLATION = False
        elif clause in ('true', 'on'):
            INTERPOLATION = True
        elif clause is None:
            INTERPOLATION = self._interpolation[-1]
        else:
            raise LanguageError("Bad interpolation setting.", clause)

        self._interpolation.append(INTERPOLATION)

        # Visit content body
        for child in children:
            body.append(self.visit(*child))

        self._switches.pop()
        self._interpolation.pop()

        slot = wrap(
            slot,
            ON_ERROR
        )
        stag.replayable = False
        if self.binds:
            try:
                bind_replay_clause = ns[(PRAMTAL, "bind-replay")]
                splits = bind_replay_clause.split(" ")
                if len(splits) != 2:
                    raise LanguageError("Invalid bind replay.", bind_replay_clause)
                events = splits[1].split(";")
                slot = BindReplay(splits[0], events, slot)
                stag.replayable = True
            except KeyError:
                pass
        if self.binds:
            try:
                bind_clause = ns[(PRAMTAL, "bind-change")]

                bind_splits = bind_clause.strip().split(" ")
                if  len(bind_splits) > 3:
                    raise LanguageError("Invalid bind syntax.", bind_clause)

                if not "#" in bind_splits[0]:
                    raise LanguageError("No bind model target specified.", bind_clause)
                first_split = bind_splits[0][1:]
                if "." in first_split:
                    bind_model,bind_ons = first_split.split(".", 1)
                    bind_ons = bind_ons.strip().split(".")
                else:
                    bind_model = first_split
                    bind_ons = []

                bind_attrs = []
                if len(bind_splits) > 1:
                    bind_attrs = bind_splits[1].split(";")

                stag.replayable = True
                slot = BindChange(bind_model, bind_ons, bind_attrs, slot )
            except KeyError:
                pass

        stag.repeatable = False
        if self.binds:
            try:
                bind_repeat_clause = ns[(PRAMTAL, "bind-repeat")]
            except KeyError:
                pass
            else:
                splits = bind_repeat_clause.split(" ")
                if len(splits) != 2:
                    raise LanguageError("Invalid define model.", bind_repeat_clause)
                if not splits[0].startswith("#"):
                    raise LanguageError("need # in alias.", bind_repeat_clause)
                slot = BindRepeat(splits[0][1:], splits[1], slot)
                stag.repeatable = True

        try:
            define_model_clause = ns[(PRAMTAL, "define-model")]
        except KeyError:
            pass
        else:
            pairs = define_model_clause.split(";")
            models = []
            for pair in pairs:
                pair = pair.strip()
                splits = pair.split(" ")
                if len(splits) != 2:
                    raise LanguageError("Invalid define model.", pair)
                if not splits[0].startswith("#"):
                    raise LanguageError("need # in alias.", pair)
                models.append((splits[0][1:], splits[1]))
            slot = DefineModel(models, slot)

        return slot
Exemplo n.º 7
0
    def visit_element(self, start, end, children):
        self.linenumber += get_newline_count(start['prefix'] + start['name'])
        if self.translatestack and self.translatestack[-1]:
            self.translatestack[-1].add_element(start)

        attributes = start['ns_attrs']
        plain_attrs = get_plain_attrs(start['attrs'])
        childs_lineno = self.linenumber
        post_offset = [x[2] for x in plain_attrs.values()]
        if post_offset:
            childs_lineno += max(post_offset)
        childs_lineno += get_newline_count(start['suffix'])
        new_domain = attributes.get((I18N_NS, 'domain'))
        old_domain = self.domainstack[-1][0] if self.domainstack else None
        new_context = attributes.get((I18N_NS, 'context'))
        old_context = self.domainstack[-1][1] if self.domainstack else None
        new_comment = attributes.get((I18N_NS, 'comment'))
        old_comment = self.domainstack[-1][2] if self.domainstack else None
        if new_domain or new_context or new_comment:
            self.domainstack.append(
                (new_domain or old_domain, new_context
                 or old_context, new_comment or old_comment))
        elif self.domainstack:
            self.domainstack.append(self.domainstack[-1])

        current_domain = self.domainstack[-1][0]
        include_domain = self.target_domain is None or self.target_domain == current_domain

        i18n_translate = attributes.get((I18N_NS, 'translate'))
        if i18n_translate is not None:
            ctx = TranslateContext(
                self.domainstack[-1][0] if self.domainstack else None,
                self.domainstack[-1][1] if self.domainstack else None,
                i18n_translate,
                self.domainstack[-1][2] if self.domainstack else None,
                self.filename, childs_lineno)
            if self.translatestack:
                ctx.parent = self.translatestack[-1]
                if ctx.parent is not None:
                    ctx.parent.register_child(start, ctx)
            self.translatestack.append(ctx)
        else:
            self.translatestack.append(None)

        if self.domainstack:
            i18n_attributes = attributes.get((I18N_NS, 'attributes'))
            if i18n_attributes and include_domain:
                parts = [p.strip() for p in i18n_attributes.split(';')]
                for msgid in parts:
                    if ' ' not in msgid:
                        if msgid not in plain_attrs:
                            continue
                        value, offset, post_offset = plain_attrs[msgid]
                        self.add_message(value,
                                         self.domainstack[-1][2] or '',
                                         offset=offset)
                    else:
                        try:
                            (attr, msgid) = msgid.split()
                        except ValueError:
                            continue
                        if attr not in plain_attrs:
                            continue
                        value, offset, post_offset = plain_attrs[attr]
                        self.add_message(msgid,
                                         u'Default: %s' % value,
                                         offset=offset)

            for (attribute, value) in attributes.items():
                value = decode_htmlentities(value)
                for source in self.get_code_for_attribute(attribute, value):
                    self.parse_python(source)

        self.linenumber = childs_lineno
        for child in children:
            self.visit(*child)

        if end is not None:
            self.linenumber += get_newline_count(end['prefix'] + end['name'])
            post_offset = [
                x[2] for x in get_plain_attrs(end['attrs']).values()
            ]
            if post_offset:
                self.linenumber += max(post_offset)
            self.linenumber += get_newline_count(end['suffix'])

        if self.domainstack:
            self.domainstack.pop()

        translate = self.translatestack.pop()
        if translate and not translate.ignore(
        ) and translate.domain and include_domain:
            self.messages.append(translate)
Exemplo n.º 8
0
    def visit_element(self, start, end, children):
        ns = start['ns_attrs']
        
        ADDITIONAL_ATTRS = {}

        for (prefix, attr), encoded in tuple(ns.items()):
            if prefix == TAL:
                ns[prefix, attr] = decode_htmlentities(encoded)
        
        # Validate namespace attributes
        validate_attributes(ns, TAL, tal.WHITELIST)
        validate_attributes(ns, METAL, metal.WHITELIST)
        validate_attributes(ns, I18N, i18n.WHITELIST)

        # Check attributes for language errors
        self._check_attributes(start['namespace'], ns)

        # Remember whitespace for item repetition
        if self._last is not None:
            self._whitespace = "\n" + " " * len(self._last.rsplit('\n', 1)[-1])

        # Set element-local whitespace
        whitespace = self._whitespace
        
        # Set up switch
        try:
            clause = ns[TAL, 'switch']
        except KeyError:
            switch = None
        else:
            value = nodes.Value(clause)
            switch = value, nodes.Copy(value)

        self._switches.append(switch)

        body = []

        # Include macro
        use_macro = ns.get((METAL, 'use-macro'))
        extend_macro = ns.get((METAL, 'extend-macro'))
        if use_macro or extend_macro:
            omit = True
            slots = []
            self._use_macro.append(slots)

            if use_macro:
                inner = nodes.UseExternalMacro(
                    nodes.Value(use_macro), slots, False
                    )
            else:
                inner = nodes.UseExternalMacro(
                    nodes.Value(extend_macro), slots, True
                    )
        # -or- include tag
        else:
            content = nodes.Sequence(body)

            # tal:content
            try:
                clause = ns[TAL, 'content']
            except KeyError:
                pass
            else:
                clause = self.get_semantic(clause, ns, 'content')

                key, value = tal.parse_substitution(clause)
                xlate = True if ns.get((I18N, 'translate')) == '' else False
                content = self._make_content_node(value, content, key, xlate)

                if end is None:
                    # Make sure start-tag has opening suffix.
                    start['suffix'] = ">"

                    # Explicitly set end-tag.
                    end = {
                        'prefix': '</',
                        'name': start['name'],
                        'space': '',
                        'suffix': '>'
                        }

            # i18n:translate
            try:
                clause = ns[I18N, 'translate']
            except KeyError:
                pass
            else:
                dynamic = ns.get((TAL, 'content')) or ns.get((TAL, 'replace'))

                if not dynamic:
                    content = nodes.Translate(clause, content)

            # tal:attributes
            try:
                clause = ns[TAL, 'attributes']
            except KeyError:
                TAL_ATTRIBUTES = []
            else:
                TAL_ATTRIBUTES = tal.parse_attributes(clause)

            # i18n:attributes
            try:
                clause = ns[I18N, 'attributes']
            except KeyError:
                I18N_ATTRIBUTES = {}
            else:
                I18N_ATTRIBUTES = i18n.parse_attributes(clause)
            
            # Prepare attributes from TAL language
            prepared = tal.prepare_attributes(
                start['attrs'], TAL_ATTRIBUTES,
                I18N_ATTRIBUTES, ns, self.DROP_NS
                )

            # Create attribute nodes
            STATIC_ATTRIBUTES = self._create_static_attributes(prepared)
            ATTRIBUTES = self._create_attributes_nodes(
                prepared, I18N_ATTRIBUTES, STATIC_ATTRIBUTES
                )

            # Start- and end nodes
            start_tag = nodes.Start(
                start['name'],
                self._maybe_trim(start['prefix']),
                self._maybe_trim(start['suffix']),
                ATTRIBUTES
                )

            end_tag = nodes.End(
                end['name'],
                end['space'],
                self._maybe_trim(end['prefix']),
                self._maybe_trim(end['suffix']),
                ) if end is not None else None

            # tal:omit-tag
            try:
                clause = ns[TAL, 'omit-tag']
            except KeyError:
                omit = False
            else:
                clause = clause.strip()

                if clause == "":
                    omit = True
                else:
                    expression = nodes.Negate(nodes.Value(clause))
                    omit = expression

                    # Wrap start- and end-tags in condition
                    start_tag = nodes.Condition(expression, start_tag)

                    if end_tag is not None:
                        end_tag = nodes.Condition(expression, end_tag)

            if omit is True or start['namespace'] in self.DROP_NS:
                inner = content
            else:
                inner = nodes.Element(
                    start_tag,
                    end_tag,
                    content,
                    )

                # Assign static attributes dictionary to "attrs" value
                inner = nodes.Define(
                    [nodes.Alias(["attrs"], STATIC_ATTRIBUTES or EMPTY_DICT)],
                    inner,
                    )

                if omit is not False:
                    inner = nodes.Cache([omit], inner)

            # tal:replace
            try:
                clause = ns[TAL, 'replace']
            except KeyError:
                pass
            else:
                clause = self.get_semantic(clause, ns, 'replace')

                key, value = tal.parse_substitution(clause)
                xlate = True if ns.get((I18N, 'translate')) == '' else False
                inner = self._make_content_node(value, inner, key, xlate)

        # metal:define-slot
        try:
            clause = ns[METAL, 'define-slot']
        except KeyError:
            DEFINE_SLOT = skip
        else:
            DEFINE_SLOT = partial(nodes.DefineSlot, clause)

        # tal:define
        try:
            clause = ns[TAL, 'define']
        except KeyError:
            DEFINE = skip
        else:
            defines = tal.parse_defines(clause)
            if defines is None:
                raise ParseError("Invalid define syntax.", clause)

            DEFINE = partial(
                nodes.Define,
                [nodes.Assignment(
                    names, nodes.Value(expr), context == "local")
                 for (context, names, expr) in defines],
                )

        # tal:case
        try:
            clause = ns[TAL, 'case']
        except KeyError:
            CASE = skip
        else:
            value = nodes.Value(clause)
            for switch in reversed(self._switches):
                if switch is not None:
                    break
            else:
                raise LanguageError(
                    "Must define switch on a parent element.", clause
                    )

            CASE = lambda node: nodes.Define(
                [nodes.Alias(["default"], switch[1], False)],
                nodes.Condition(
                    nodes.Equality(switch[0], value),
                    nodes.Cancel([switch[0]], node),
                ))

        # tal:repeat
        try:
            clause = ns[TAL, 'repeat']
        except KeyError:
            REPEAT = skip
        else:
            defines = tal.parse_defines(clause)
            assert len(defines) == 1
            context, names, expr = defines[0]

            expression = nodes.Value(expr)

            if start['namespace'] == TAL:
                self._last = None
                self._whitespace = whitespace.lstrip('\n')
                whitespace = ""

            REPEAT = partial(
                nodes.Repeat,
                names,
                expression,
                context == "local",
                whitespace
                )

        # tal:condition
        try:
            clause = ns[TAL, 'condition']
        except KeyError:
            CONDITION = skip
        else:
            expression = nodes.Value(clause)
            CONDITION = partial(nodes.Condition, expression)

        # tal:switch
        if switch is None:
            SWITCH = skip
        else:
            SWITCH = partial(nodes.Cache, list(switch))

        # i18n:domain
        try:
            clause = ns[I18N, 'domain']
        except KeyError:
            DOMAIN = skip
        else:
            DOMAIN = partial(nodes.Domain, clause)

        # i18n:name
        try:
            clause = ns[I18N, 'name']
        except KeyError:
            NAME = skip
        else:
            if not clause.strip():
                NAME = skip
            else:
                NAME = partial(nodes.Name, clause)

        # The "slot" node next is the first node level that can serve
        # as a macro slot
        slot = wrap(
            inner,
            DEFINE_SLOT,
            DEFINE,
            CASE,
            CONDITION,
            REPEAT,
            SWITCH,
            DOMAIN,
            )

        # metal:fill-slot
        try:
            clause = ns[METAL, 'fill-slot']
        except KeyError:
            pass
        else:
            if not clause.strip():
                raise LanguageError(
                    "Must provide a non-trivial string for metal:fill-slot.",
                    clause
                )

            index = -(1 + int(bool(use_macro or extend_macro)))

            try:
                slots = self._use_macro[index]
            except IndexError:
                raise LanguageError(
                    "Cannot use metal:fill-slot without metal:use-macro.",
                    clause
                    )

            slots = self._use_macro[index]
            slots.append(nodes.FillSlot(clause, slot))

        # metal:define-macro
        try:
            clause = ns[METAL, 'define-macro']
        except KeyError:
            pass
        else:
            self._macros[clause] = slot
            slot = nodes.UseInternalMacro(clause)

        slot = wrap(
            slot,
            NAME
            )

        # tal:on-error
        try:
            clause = ns[TAL, 'on-error']
        except KeyError:
            ON_ERROR = skip
        else:
            key, value = tal.parse_substitution(clause)
            translate = True if ns.get((I18N, 'translate')) == '' else False

            fallback = self._make_content_node(value, None, key, translate)

            if omit is False and start['namespace'] not in self.DROP_NS:
                start_tag = copy(start_tag)

                start_tag.attributes = nodes.Sequence(
                    start_tag.attributes.extract(
                        lambda attribute:
                        isinstance(attribute, nodes.Attribute) and
                        isinstance(attribute.expression, ast.Str)
                    )
                )

                if end_tag is None:
                    # Make sure start-tag has opening suffix. We don't
                    # allow self-closing element here.
                    start_tag.suffix = ">"

                    # Explicitly set end-tag.
                    end_tag = nodes.End(start_tag.name, '', '</', '>',)

                fallback = nodes.Element(
                    start_tag,
                    end_tag,
                    fallback,
                )

            ON_ERROR = partial(nodes.OnError, fallback, 'error')

        clause = ns.get((META, 'interpolation'))
        if clause in ('false', 'off'):
            INTERPOLATION = False
        elif clause in ('true', 'on'):
            INTERPOLATION = True
        elif clause is None:
            INTERPOLATION = self._interpolation[-1]
        else:
            raise LanguageError("Bad interpolation setting.", clause)

        self._interpolation.append(INTERPOLATION)

        # Visit content body
        for child in children:
            body.append(self.visit(*child))

        self._switches.pop()
        self._interpolation.pop()

        if use_macro:
            self._use_macro.pop()

        return wrap(
            slot,
            ON_ERROR
            )
Exemplo n.º 9
0
    def prev_visit_element(self, start, end, children):
        ns = start['ns_attrs']

        for (prefix, attr), encoded in tuple(ns.items()):
            if prefix == TAL:
                ns[prefix, attr] = decode_htmlentities(encoded)

        # Validate namespace attributes
        validate_attributes(ns, TAL, tal.WHITELIST)
        validate_attributes(ns, I18N, i18n.WHITELIST)

        # Check attributes for language errors
        self._check_attributes(start['namespace'], ns)

        # Remember whitespace for item repetition
        if self._last is not None:
            self._whitespace = "\n" + " " * len(self._last.rsplit('\n', 1)[-1])

        # Set element-local whitespace
        whitespace = self._whitespace

        # Set up switch
        try:
            clause = ns[TAL, 'switch']
        except KeyError:
            switch = None
        else:
            switch = nodes.Value(clause)

        self._switches.append(switch)

        body = []

        content = nodes.Sequence(body)

        # tal:content
        try:
            clause = ns[TAL, 'content']
        except KeyError:
            pass
        else:
            key, value = tal.parse_substitution(clause)
            xlate = True if ns.get((I18N, 'translate')) == '' else False
            content = self._make_content_node(value, content, key, xlate)

            if end is None:
                # Make sure start-tag has opening suffix.
                start['suffix'] = ">"

                # Explicitly set end-tag.
                end = {
                    'prefix': '</',
                    'name': start['name'],
                    'space': '',
                    'suffix': '>'
                }

        # i18n:translate
        try:
            clause = ns[I18N, 'translate']
        except KeyError:
            pass
        else:
            dynamic = ns.get((TAL, 'content')) or ns.get((TAL, 'replace'))

            if not dynamic:
                content = nodes.Translate(clause, content)

        # tal:attributes
        try:
            clause = ns[TAL, 'attributes']
        except KeyError:
            TAL_ATTRIBUTES = {}
        else:
            TAL_ATTRIBUTES = tal.parse_attributes(clause)

        # i18n:attributes
        try:
            clause = ns[I18N, 'attributes']
        except KeyError:
            I18N_ATTRIBUTES = {}
        else:
            I18N_ATTRIBUTES = i18n.parse_attributes(clause)

        # Prepare attributes from TAL language
        prepared = tal.prepare_attributes(start['attrs'], TAL_ATTRIBUTES,
                                          I18N_ATTRIBUTES, ns, self.DROP_NS)

        # Create attribute nodes
        STATIC_ATTRIBUTES = self._create_static_attributes(prepared)
        ATTRIBUTES = self._create_attributes_nodes(prepared, I18N_ATTRIBUTES)

        # Start- and end nodes
        start_tag = nodes.Start(start['name'],
                                self._maybe_trim(start['prefix']),
                                self._maybe_trim(start['suffix']), ATTRIBUTES)
        stag = start_tag

        end_tag = nodes.End(
            end['name'],
            end['space'],
            self._maybe_trim(end['prefix']),
            self._maybe_trim(end['suffix']),
        ) if end is not None else None

        # tal:omit-tag
        try:
            clause = ns[TAL, 'omit-tag']
        except KeyError:
            omit = False
        else:
            clause = clause.strip()

            if clause == "":
                omit = True
            else:
                expression = nodes.Negate(nodes.Value(clause))
                omit = expression

                # Wrap start- and end-tags in condition
                start_tag = nodes.Condition(expression, start_tag)

                if end_tag is not None:
                    end_tag = nodes.Condition(expression, end_tag)

        if omit is True or start['namespace'] in self.DROP_NS:
            inner = content
        else:
            inner = nodes.Element(
                start_tag,
                end_tag,
                content,
            )

            # Assign static attributes dictionary to "attrs" value
            inner = nodes.Define(
                [nodes.Alias(["attrs"], STATIC_ATTRIBUTES)],
                inner,
            )

            if omit is not False:
                inner = nodes.Cache([omit], inner)

        # tal:replace
        try:
            clause = ns[TAL, 'replace']
        except KeyError:
            pass
        else:
            key, value = tal.parse_substitution(clause)
            xlate = True if ns.get((I18N, 'translate')) == '' else False
            inner = self._make_content_node(value, inner, key, xlate)

        # tal:define
        try:
            clause = ns[TAL, 'define']
        except KeyError:
            DEFINE = skip
        else:
            defines = tal.parse_defines(clause)
            if defines is None:
                raise ParseError("Invalid define syntax.", clause)

            DEFINE = partial(
                nodes.Define,
                [
                    nodes.Assignment(names, nodes.Value(expr), context
                                     == "local")
                    for (context, names, expr) in defines
                ],
            )

        # tal:case
        try:
            clause = ns[TAL, 'case']
        except KeyError:
            CASE = skip
        else:
            value = nodes.Value(clause)
            for switch in reversed(self._switches):
                if switch is not None:
                    break
            else:
                raise LanguageError("Must define switch on a parent element.",
                                    clause)

            CASE = lambda node: nodes.Define([
                nodes.Assignment(["default"], switch, True)
            ], nodes.Condition(
                nodes.Equality(switch, value),
                node,
            ))

        # tal:repeat
        try:
            clause = ns[TAL, 'repeat']
        except KeyError:
            REPEAT = skip
        else:
            defines = tal.parse_defines(clause)
            assert len(defines) == 1
            context, names, expr = defines[0]

            expression = nodes.Value(expr)

            REPEAT = partial(nodes.Repeat, names, expression,
                             context == "local", whitespace)

        # tal:condition
        try:
            clause = ns[TAL, 'condition']
        except KeyError:
            CONDITION = skip
        else:
            expression = nodes.Value(clause)
            CONDITION = partial(nodes.Condition, expression)

        # tal:switch
        if switch is None:
            SWITCH = skip
        else:
            SWITCH = partial(nodes.Cache, [switch])

        # i18n:domain
        try:
            clause = ns[I18N, 'domain']
        except KeyError:
            DOMAIN = skip
        else:
            DOMAIN = partial(nodes.Domain, clause)

        # i18n:name
        try:
            clause = ns[I18N, 'name']
        except KeyError:
            NAME = skip
        else:
            NAME = partial(nodes.Name, clause)

        # The "slot" node next is the first node level that can serve
        # as a macro slot
        slot = wrap(
            inner,
            DEFINE,
            CASE,
            CONDITION,
            REPEAT,
            SWITCH,
            DOMAIN,
        )

        slot = wrap(slot, NAME)

        # tal:on-error
        try:
            clause = ns[TAL, 'on-error']
        except KeyError:
            ON_ERROR = skip
        else:
            key, value = tal.parse_substitution(clause)
            translate = True if ns.get((I18N, 'translate')) == '' else False

            fallback = self._make_content_node(value, None, key, translate)

            if omit is False and start['namespace'] not in self.DROP_NS:
                fallback = nodes.Element(
                    start_tag,
                    end_tag,
                    fallback,
                )

            ON_ERROR = partial(nodes.OnError, fallback, 'error')

        clause = ns.get((META, 'interpolation'))
        if clause in ('false', 'off'):
            INTERPOLATION = False
        elif clause in ('true', 'on'):
            INTERPOLATION = True
        elif clause is None:
            INTERPOLATION = self._interpolation[-1]
        else:
            raise LanguageError("Bad interpolation setting.", clause)

        self._interpolation.append(INTERPOLATION)

        # Visit content body
        for child in children:
            body.append(self.visit(*child))

        self._switches.pop()
        self._interpolation.pop()

        slot = wrap(slot, ON_ERROR)
        stag.replayable = False
        if self.binds:
            try:
                bind_replay_clause = ns[(PRAMTAL, "bind-replay")]
                splits = bind_replay_clause.split(" ")
                if len(splits) != 2:
                    raise LanguageError("Invalid bind replay.",
                                        bind_replay_clause)
                events = splits[1].split(";")
                slot = BindReplay(splits[0], events, slot)
                stag.replayable = True
            except KeyError:
                pass
        if self.binds:
            try:
                bind_clause = ns[(PRAMTAL, "bind-change")]

                bind_splits = bind_clause.strip().split(" ")
                if len(bind_splits) > 3:
                    raise LanguageError("Invalid bind syntax.", bind_clause)

                if not "#" in bind_splits[0]:
                    raise LanguageError("No bind model target specified.",
                                        bind_clause)
                first_split = bind_splits[0][1:]
                if "." in first_split:
                    bind_model, bind_ons = first_split.split(".", 1)
                    bind_ons = bind_ons.strip().split(".")
                else:
                    bind_model = first_split
                    bind_ons = []

                bind_attrs = []
                if len(bind_splits) > 1:
                    bind_attrs = bind_splits[1].split(";")

                stag.replayable = True
                slot = BindChange(bind_model, bind_ons, bind_attrs, slot)
            except KeyError:
                pass

        stag.repeatable = False
        if self.binds:
            try:
                bind_repeat_clause = ns[(PRAMTAL, "bind-repeat")]
            except KeyError:
                pass
            else:
                splits = bind_repeat_clause.split(" ")
                if len(splits) != 2:
                    raise LanguageError("Invalid define model.",
                                        bind_repeat_clause)
                if not splits[0].startswith("#"):
                    raise LanguageError("need # in alias.", bind_repeat_clause)
                slot = BindRepeat(splits[0][1:], splits[1], slot)
                stag.repeatable = True

        try:
            define_model_clause = ns[(PRAMTAL, "define-model")]
        except KeyError:
            pass
        else:
            pairs = define_model_clause.split(";")
            models = []
            for pair in pairs:
                pair = pair.strip()
                splits = pair.split(" ")
                if len(splits) != 2:
                    raise LanguageError("Invalid define model.", pair)
                if not splits[0].startswith("#"):
                    raise LanguageError("need # in alias.", pair)
                models.append((splits[0][1:], splits[1]))
            slot = DefineModel(models, slot)

        return slot
Exemplo n.º 10
0
    def __call__(self, name, engine):
        """The strategy is to find possible expression strings and
        call the ``validate`` function of the parser to validate.

        For every possible starting point, the longest possible
        expression is tried first, then the second longest and so
        forth.

        Example 1:

          ${'expressions use the ${<expression>} format'}

        The entire expression is attempted first and it is also the
        only one that validates.

        Example 2:

          ${'Hello'} ${'world!'}

        Validation of the longest possible expression (the entire
        string) will fail, while the second round of attempts,
        ``${'Hello'}`` and ``${'world!'}`` respectively, validate.

        """

        body = []
        nodes = []
        text = self.expression

        expr_map = {}
        translate = self.translate

        while text:
            matched = text
            m = self.regex.search(matched)
            if m is None:
                nodes.append(ast.Str(s=text))
                break

            part = text[:m.start()]
            text = text[m.start():]

            if part:
                node = ast.Str(s=part)
                nodes.append(node)

            if not body:
                target = name
            else:
                target = store("%s_%d" % (name.id, text.pos))

            while True:
                d = groupdict(m, matched)
                string = d["expression"] or d["variable"] or ""

                string = decode_htmlentities(string)

                try:
                    compiler = engine.parse(string)
                    body += compiler.assign_text(target)
                except ExpressionError:
                    matched = matched[m.start():m.end() - 1]
                    m = self.regex.search(matched)
                    if m is None:
                        raise
                else:
                    break

            # If one or more expressions are not simple names, we
            # disable translation.
            if RE_NAME.match(string) is None:
                translate = False

            # if this is the first expression, use the provided
            # assignment name; otherwise, generate one (here based
            # on the string position)
            node = load(target.id)
            nodes.append(node)

            expr_map[node] = safe_native(string)

            text = text[len(m.group()):]

        if len(nodes) == 1:
            target = nodes[0]

            if translate and isinstance(target, ast.Str):
                target = template(
                    "translate(msgid, domain=__i18n_domain)",
                    msgid=target, mode="eval",
                    )
        else:
            if translate:
                formatting_string = ""
                keys = []
                values = []

                for node in nodes:
                    if isinstance(node, ast.Str):
                        formatting_string += node.s
                    else:
                        string = expr_map[node]
                        formatting_string += "${%s}" % string
                        keys.append(ast.Str(s=string))
                        values.append(node)

                target = template(
                    "translate(msgid, mapping=mapping, domain=__i18n_domain)",
                    msgid=ast.Str(s=formatting_string),
                    mapping=ast.Dict(keys=keys, values=values),
                    mode="eval"
                    )
            else:
                nodes = [
                    template(
                        "NODE",
                        NODE=node, mode="eval"
                        )
                    for node in nodes
                    ]

                target = ast.BinOp(
                    left=ast.Str(s="%s" * len(nodes)),
                    op=ast.Mod(),
                    right=ast.Tuple(elts=nodes, ctx=ast.Load()))
            body += [ast.Assign(targets=[name], value=target)]

        return body
Exemplo n.º 11
0
    def __call__(self, name, engine):
        """The strategy is to find possible expression strings and
        call the ``validate`` function of the parser to validate.

        For every possible starting point, the longest possible
        expression is tried first, then the second longest and so
        forth.

        Example 1:

          ${'expressions use the ${<expression>} format'}

        The entire expression is attempted first and it is also the
        only one that validates.

        Example 2:

          ${'Hello'} ${'world!'}

        Validation of the longest possible expression (the entire
        string) will fail, while the second round of attempts,
        ``${'Hello'}`` and ``${'world!'}`` respectively, validate.

        """

        body = []
        nodes = []
        text = self.expression

        expr_map = {}
        translate = self.translate

        while text:
            matched = text
            m = self.regex.search(matched)
            if m is None:
                nodes.append(ast.Str(s=text))
                break

            part = text[:m.start()]
            text = text[m.start():]

            if part:
                node = ast.Str(s=part)
                nodes.append(node)

            if not body:
                target = name
            else:
                target = store("%s_%d" % (name.id, text.pos))

            while True:
                d = groupdict(m, matched)
                string = d["expression"] or d["variable"] or ""

                string = decode_htmlentities(string)

                try:
                    compiler = engine.parse(string)
                    body += compiler.assign_text(target)
                except ExpressionError:
                    matched = matched[m.start():m.end() - 1]
                    m = self.regex.search(matched)
                    if m is None:
                        raise
                else:
                    break

            # If one or more expressions are not simple names, we
            # disable translation.
            if RE_NAME.match(string) is None:
                translate = False

            # if this is the first expression, use the provided
            # assignment name; otherwise, generate one (here based
            # on the string position)
            node = load(target.id)
            nodes.append(node)

            expr_map[node] = safe_native(string)

            text = text[len(m.group()):]

        if len(nodes) == 1:
            target = nodes[0]

            if translate and isinstance(target, ast.Str):
                target = template(
                    "translate(msgid, domain=__i18n_domain)",
                    msgid=target,
                    mode="eval",
                )
        else:
            if translate:
                formatting_string = ""
                keys = []
                values = []

                for node in nodes:
                    if isinstance(node, ast.Str):
                        formatting_string += node.s
                    else:
                        string = expr_map[node]
                        formatting_string += "${%s}" % string
                        keys.append(ast.Str(s=string))
                        values.append(node)

                target = template(
                    "translate(msgid, mapping=mapping, domain=__i18n_domain)",
                    msgid=ast.Str(s=formatting_string),
                    mapping=ast.Dict(keys=keys, values=values),
                    mode="eval")
            else:
                nodes = [
                    template("NODE", NODE=node, mode="eval") for node in nodes
                ]

                target = ast.BinOp(left=ast.Str(s="%s" * len(nodes)),
                                   op=ast.Mod(),
                                   right=ast.Tuple(elts=nodes, ctx=ast.Load()))
            body += [ast.Assign(targets=[name], value=target)]

        return body