Example #1
0
    def translate(self, expression, target):
        if isinstance(target, string_type):
            target = store(target)

        cached = self.cache.get(expression)

        if cached is not None:
            stmts = [ast.Assign(targets=[target], value=cached)]
        elif isinstance(expression, ast.expr):
            stmts = [ast.Assign(targets=[target], value=expression)]
        else:
            # The engine interface supports simple strings, which
            # default to expression nodes
            if isinstance(expression, string_type):
                expression = Value(expression, True)

            kind = type(expression).__name__
            visitor = getattr(self, "visit_%s" % kind)
            stmts = visitor(expression, target)

            # Add comment
            target_id = getattr(target, "id", target)
            comment = Comment(" %r -> %s" % (expression, target_id))
            stmts.insert(0, comment)

        return stmts
Example #2
0
    def visit_Assignment(self, node):
        for name in node.names:
            if name in COMPILER_INTERNALS_OR_DISALLOWED:
                raise TranslationError("Name disallowed by compiler.", name)

            if name.startswith('__'):
                raise TranslationError(
                    "Name disallowed by compiler (double underscore).", name)

        assignment = self._engine(node.expression, store("__value"))

        if len(node.names) != 1:
            target = ast.Tuple(
                elts=[store_econtext(name) for name in node.names],
                ctx=ast.Store(),
            )
        else:
            target = store_econtext(node.names[0])

        assignment.append(ast.Assign(targets=[target], value=load("__value")))

        for name in node.names:
            if not node.local:
                assignment += template("rcontext[KEY] = __value",
                                       KEY=ast.Str(s=native_string(name)))

        return assignment
Example #3
0
 def visit_Builtin(self, node, target):
     value = annotated(node)
     return [ast.Assign(targets=[target], value=value)]
Example #4
0
 def visit_Default(self, node, target):
     value = annotated(node.marker)
     return [ast.Assign(targets=[target], value=value)]
Example #5
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
Example #6
0
    def visit_Repeat(self, node):
        # Used for loop variable definition and restore
        self._scopes.append(set())

        # Variable assignment and repeat key for single- and
        # multi-variable repeat clause
        if node.local:
            contexts = "econtext",
        else:
            contexts = "econtext", "rcontext"

        for name in node.names:
            if name in COMPILER_INTERNALS_OR_DISALLOWED:
                raise TranslationError("Name disallowed by compiler.", name)

        if len(node.names) > 1:
            targets = [
                ast.Tuple(elts=[
                    subscript(native_string(name), load(context), ast.Store())
                    for name in node.names
                ],
                          ctx=ast.Store()) for context in contexts
            ]

            key = ast.Tuple(elts=[ast.Str(s=name) for name in node.names],
                            ctx=ast.Load())
        else:
            name = node.names[0]
            targets = [
                subscript(native_string(name), load(context), ast.Store())
                for context in contexts
            ]

            key = ast.Str(s=node.names[0])

        repeat = identifier("__repeat", id(node))
        index = identifier("__index", id(node))
        iterator = identifier("__iterator", id(node))
        assignment = [ast.Assign(targets=targets, value=load("__item"))]

        # Make repeat assignment in outer loop
        names = node.names
        local = node.local

        outer = self._engine(node.expression, store(iterator))

        if local:
            outer[:] = list(self._enter_assignment(names)) + outer

        outer += template("REPEAT = econtext['repeat'](key, ITERATOR)",
                          key=key,
                          INDEX=index,
                          REPEAT=repeat,
                          ITERATOR=iterator)
        outer += template("INDEX = REPEAT.length", INDEX=index, REPEAT=repeat)

        # Set a trivial default value for each name assigned to make
        # sure we assign a value even if the iteration is empty
        for name in node.names:
            outer += [
                ast.Assign(targets=[store_econtext(name)], value=load("None"))
            ]

        inner = template("REPEAT._next()", REPEAT=repeat)
        # Compute inner body
        inner += self.visit(node.node)

        # After each iteration, decrease the index
        inner += template("index -= 1", index=index)

        # For items up to N - 1, emit repeat whitespace
        inner += template("if INDEX > 0: STACK.t(WHITESPACE)",
                          STACK=self._current_stack,
                          INDEX=index,
                          WHITESPACE=ast.Str(s=node.whitespace))

        # Main repeat loop
        outer += [
            ast.For(
                target=store("__item"),
                iter=load(iterator),
                body=assignment + inner,
            )
        ]

        # Finally, clean up assignment if it's local
        if outer:
            outer += self._leave_assignment(names)

        self._scopes.pop()

        return outer
Example #7
0
    def visit_Translate(self, node):
        """Translation.

        Visit items and assign output to a default value.

        Finally, compile a translation expression and use either
        result or default.
        """

        body = []

        # Track the blocks of this translation
        self._translations.append(set())

        # Prepare new stream
        append = identifier("append", id(node))
        stream = identifier("stream", id(node))
        body += template("s = new_list", s=stream, new_list=LIST) + \
                template("a = s.append", a=append, s=stream)

        # Visit body to generate the message body
        code = self.visit(node.node)
        swap(ast.Suite(body=code), load(append), "__append")
        body += code

        # Reduce white space and assign as message id
        msgid = identifier("msgid", id(node))
        body += template("msgid = __re_whitespace(''.join(stream)).strip()",
                         msgid=msgid,
                         stream=stream)

        default = msgid

        # Compute translation block mapping if applicable
        names = self._translations[-1]
        if names:
            keys = []
            values = []

            for name in names:
                stream, append = self._get_translation_identifiers(name)
                keys.append(ast.Str(s=name))
                values.append(load(stream))

                # Initialize value
                body.insert(
                    0,
                    ast.Assign(targets=[store(stream)],
                               value=ast.Str(s=native_string(""))))

            mapping = ast.Dict(keys=keys, values=values)
        else:
            mapping = None

        # if this translation node has a name, use it as the message id
        if node.msgid:
            msgid = ast.Str(s=node.msgid)

        # emit the translation expression
        body += template(
            "STACK.t(translate("
            "msgid, mapping=mapping, default=default, domain=__i18n_domain))",
            stack=self._current_stack,
            msgid=msgid,
            default=default,
            mapping=mapping)

        # pop away translation block reference
        self._translations.pop()

        return body