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

        try:
            stmts = self.translate(expression, target)
        except ExpressionError:
            if self.strict:
                raise

            exc = sys.exc_info()[1]
            p = pickle.dumps(exc)

            stmts = template(
                "__exc = loads(p)", loads=self.loads_symbol, p=ast.Str(s=p)
                )

            token = Token(exc.token, exc.offset, filename=exc.filename)

            stmts += set_error(token, load("__exc"))
            stmts += [ast.Raise(exc=load("__exc"))]

        # Apply visitor to each statement
        for stmt in stmts:
            self.visitor(stmt)

        return stmts
Example #2
0
    def __call__(self, expression, target):
        if isinstance(target, string_type):
            target = store(target)

        try:
            stmts = self.translate(expression, target)
        except ExpressionError:
            if self.strict:
                raise

            exc = sys.exc_info()[1]
            p = pickle.dumps(exc)

            stmts = template("__exc = loads(p)",
                             loads=self.loads_symbol,
                             p=ast.Str(s=p))

            token = Token(exc.token, exc.offset, filename=exc.filename)

            stmts += set_error(token, load("__exc"))
            stmts += [ast.Raise(exc=load("__exc"))]

        # Apply visitor to each statement
        for stmt in stmts:
            self.visitor(stmt)

        return stmts
Example #3
0
    def __call__(self, node):
        name = node.id

        # Don't rewrite names that begin with an underscore; they are
        # internal and can be assumed to be locally defined. This
        # policy really should be part of the template program, not
        # defined here in the compiler.

        if isinstance(node.ctx, ast.Load):
            if self.validate_model and name.startswith("__model_"):
                name = node.id[8:]
                if not name in self.defined_models:
                    err = ParseError(
                        "Cannot find model",
                        Token(name,
                              pos=node.lineno,
                              source=self.source,
                              filename=self.filename))
                    raise err
                node.id = self.defined_models[name]
                return node

        if name.startswith('__') or name in self.internals:
            return node

        if name == "el":
            return Builtin("None")

        if isinstance(node.ctx, ast.Store):
            return store_econtext(name)

        aliased = self.aliases.get(name)
        if aliased is not None:
            return load(aliased)

        # If the name is a Python global, first try acquiring it from
        # the dynamic context, then fall back to the global.
        if name in self.builtins:
            return template(
                "getitem(econtext, key, name)",
                getitem=Symbol(getitem),
                mode="eval",
                key=ast.Str(s=name),
                name=load(name),
            )

        # Otherwise, simply acquire it from the dynamic context.
        return load_econtext(name)
Example #4
0
    def visit_BindRepeat(self, node):
        body = []

        body.append(Comment("start model-repeat-binding"))

        new_stack = identifier("stack", id(node))
        old_stack = self._current_stack
        body += template("CAPTURED_STACK = STACK.capture_for_repeat()",
                         CAPTURED_STACK=new_stack,
                         STACK=old_stack)

        new_stack = identifier("stack", id(node))

        self._current_stack = new_stack
        self._aliases.append(self._aliases[-1].copy())

        model_name = identifier("model_%s" % node.alias, id(node))
        self._defined_models[node.alias] = model_name

        inner_on_add = []
        inner_on_add += self.visit(node.node)
        inner_on_add += template("return STACK.repeat_el",
                                 STACK=self._current_stack)

        on_add_name = identifier("on_add", id(node))
        on_add_func = [
            ast.FunctionDef(name=on_add_name,
                            args=ast.arguments(
                                args=[load(model_name)],
                                defaults=(),
                            ),
                            body=inner_on_add)
        ]
        body += on_add_func

        collection = "__collection"
        initializer = self._engine(node.expression, store(collection))
        initializer += template("BIND_REPEAT(COLLECTION, ON_ADD)",
                                BIND_REPEAT=Symbol(bind_repeat),
                                COLLECTION=load(collection),
                                ON_ADD=on_add_name)
        body += initializer

        self._aliases.pop()
        self._current_stack = old_stack

        body.append(Comment("end model-repeat-binding"))
        return body
Example #5
0
    def visit_Name(self, node):
        """Translation name."""

        if not self._translations:
            raise TranslationError("Not allowed outside of translation.",
                                   node.name)

        if node.name in self._translations[-1]:
            raise TranslationError("Duplicate translation name: %s." %
                                   node.name)

        self._translations[-1].add(node.name)
        body = []

        # prepare new stream
        stream, append = self._get_translation_identifiers(node.name)
        body += template("s = new_list", s=stream, new_list=LIST) + \
                template("a = s.append", a=append, s=stream)

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

        # output msgid
        text = Text('${%s}' % node.name)
        body += self.visit(text)

        # Concatenate stream
        body += template("stream = ''.join(stream)", stream=stream)

        return body
Example #6
0
    def visit_Name(self, node):
        """Translation name."""

        if not self._translations:
            raise TranslationError(
                "Not allowed outside of translation.", node.name)

        if node.name in self._translations[-1]:
            raise TranslationError(
                "Duplicate translation name: %s." % node.name)

        self._translations[-1].add(node.name)
        body = []

        # prepare new stream
        stream, append = self._get_translation_identifiers(node.name)
        body += template("s = new_list", s=stream, new_list=LIST) + \
                template("a = s.append", a=append, s=stream)

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

        # output msgid
        text = Text('${%s}' % node.name)
        body += self.visit(text)

        # Concatenate stream
        body += template("stream = ''.join(stream)", stream=stream)

        return body
Example #7
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 #8
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 #9
0
    def __call__(self, node):
        name = node.id

        # Don't rewrite names that begin with an underscore; they are
        # internal and can be assumed to be locally defined. This
        # policy really should be part of the template program, not
        # defined here in the compiler.

        if isinstance(node.ctx, ast.Load):
            if self.validate_model and name.startswith("__model_"):
                name = node.id[8:]
                if not name in self.defined_models:
                    err = ParseError(
                        "Cannot find model", Token(name, pos=node.lineno, source=self.source, filename=self.filename))
                    raise err
                node.id = self.defined_models[name]
                return node

        if name.startswith('__') or name in self.internals:
            return node

        if name == "el":
            return Builtin("None")

        if isinstance(node.ctx, ast.Store):
            return store_econtext(name)

        aliased = self.aliases.get(name)
        if aliased is not None:
            return load(aliased)

        # If the name is a Python global, first try acquiring it from
        # the dynamic context, then fall back to the global.
        if name in self.builtins:
            return template(
                "getitem(econtext, key, name)",
                getitem=Symbol(getitem),
                mode="eval",
                key=ast.Str(s=name),
                name=load(name),
                )

        # Otherwise, simply acquire it from the dynamic context.
        return load_econtext(name)
Example #10
0
    def visit_BindRepeat(self, node):
        body = []

        body.append(Comment("start model-repeat-binding"))

        new_stack = identifier("stack", id(node))
        old_stack = self._current_stack
        body += template("CAPTURED_STACK = STACK.capture_for_repeat()", CAPTURED_STACK=new_stack, STACK=old_stack)

        new_stack = identifier("stack", id(node))

        self._current_stack = new_stack
        self._aliases.append(self._aliases[-1].copy())

        model_name = identifier("model_%s" % node.alias, id(node))
        self._defined_models[node.alias] = model_name

        inner_on_add = []
        inner_on_add += self.visit(node.node)
        inner_on_add += template("return STACK.repeat_el", STACK=self._current_stack)

        on_add_name = identifier("on_add", id(node))
        on_add_func = [ast.FunctionDef(
            name=on_add_name, args=ast.arguments(
                args=[load(model_name)],
                defaults=(),
            ),
            body=inner_on_add
        )]
        body += on_add_func

        collection = "__collection"
        initializer = self._engine(node.expression, store(collection))
        initializer += template("BIND_REPEAT(COLLECTION, ON_ADD)",
            BIND_REPEAT=Symbol(bind_repeat), COLLECTION=load(collection), ON_ADD=on_add_name)
        body += initializer

        self._aliases.pop()
        self._current_stack = old_stack

        body.append(Comment("end model-repeat-binding"))
        return body
Example #11
0
    def translate(self, string, target):
        """
        >>> from chameleon.tales import test
        >>> test(PathExpr('None')) is None
        True
        """
        string = string.strip()

        if not string:
            return template("target = None", target=target)

        m = self.path_regex.match(string)
        if m is None:
            raise ExpressionError("Not a valid path-expression.", string)

        nocall, path = m.groups()

        # note that unicode paths are not allowed
        parts = str(path).split("/")

        components = self._find_translation_components(parts)

        base = parts[0]

        if not components:
            if len(parts) == 1 and (nocall or base == "None"):
                return template("target = base", base=base, target=target)
            else:
                components = ()

        call = template(
            "traverse(base, econtext, call, path_items)",
            traverse=self.traverser,
            base=load(base),
            call=load(str(not nocall)),
            path_items=ast.Tuple(elts=components),
            mode="eval",
        )

        return template("target = value", target=target, value=call)
Example #12
0
    def translate(self, string, target):
        """
        >>> from chameleon.tales import test
        >>> test(PathExpr('None')) is None
        True
        """
        string = string.strip()

        if not string:
            return template("target = None", target=target)

        m = self.path_regex.match(string)
        if m is None:
            raise ExpressionError("Not a valid path-expression.", string)

        nocall, path = m.groups()

        # note that unicode paths are not allowed
        parts = str(path).split("/")

        components = self._find_translation_components(parts)

        base = parts[0]

        if not components:
            if len(parts) == 1 and (nocall or base == "None"):
                return template("target = base", base=base, target=target)
            else:
                components = ()

        call = template(
            "traverse(base, econtext, call, path_items)",
            traverse=self.traverser,
            base=load(base),
            call=load(str(not nocall)),
            path_items=ast.Tuple(elts=components),
            mode="eval",
        )

        return template("target = value", target=target, value=call)
Example #13
0
    def visit_BindChange(self, node):
        body = []

        body.append(Comment("start model-binding"))

        new_stack = identifier("stack", id(node))
        old_stack = self._current_stack
        body += template("CAPTURED_STACK = STACK.capture()",
                         CAPTURED_STACK=new_stack,
                         STACK=old_stack)

        self._current_stack = new_stack
        self._aliases.append(self._aliases[-1].copy())

        inner = self.visit(node.node)

        self._aliases.pop()
        self._current_stack = old_stack

        on_change_name = identifier("on_change", id(node))
        on_change_func = [
            ast.FunctionDef(name=on_change_name,
                            args=ast.arguments(
                                args=[],
                                defaults=(),
                            ),
                            body=inner)
        ]
        if node.model_name not in self._defined_models:
            raise TranslationError(
                "Cannot find bind model on current context.", node.model_name)

        body += on_change_func
        bind_attrs = ast.Tuple(
            elts=[ast.Str(s=attr) for attr in node.bind_attrs], ctx=ast.Load())
        bind_ons = ast.Tuple(elts=[ast.Str(s=attr) for attr in node.bind_ons],
                             ctx=ast.Load())
        body += template("BIND_CHANGE(MODEL, BIND_ONS, BIND_ATTRS, ON_CHANGE)",
                         BIND_CHANGE=Symbol(bind_change),
                         MODEL=load(self._defined_models[node.model_name]),
                         BIND_ATTRS=bind_attrs,
                         BIND_ONS=bind_ons,
                         ON_CHANGE=on_change_name)

        body.append(Comment("end model-binding"))
        return body
Example #14
0
    def visit_Condition(self, node):
        target = "__condition"
        assignment = self._engine(node.expression, target)

        assert assignment

        for stmt in assignment:
            yield stmt

        body = self.visit(node.node) or [ast.Pass()]

        orelse = getattr(node, "orelse", None)
        if orelse is not None:
            orelse = self.visit(orelse)

        test = load(target)

        yield ast.If(test=test, body=body, orelse=orelse)
Example #15
0
    def visit_Condition(self, node):
        target = "__condition"
        assignment = self._engine(node.expression, target)

        assert assignment

        for stmt in assignment:
            yield stmt

        body = self.visit(node.node) or [ast.Pass()]

        orelse = getattr(node, "orelse", None)
        if orelse is not None:
            orelse = self.visit(orelse)

        test = load(target)

        yield ast.If(test=test, body=body, orelse=orelse)
Example #16
0
    def visit_BindChange(self, node):
        body = []

        body.append(Comment("start model-binding"))

        new_stack = identifier("stack", id(node))
        old_stack = self._current_stack
        body += template("CAPTURED_STACK = STACK.capture()", CAPTURED_STACK=new_stack, STACK=old_stack)

        self._current_stack = new_stack
        self._aliases.append(self._aliases[-1].copy())

        inner = self.visit(node.node)

        self._aliases.pop()
        self._current_stack = old_stack

        on_change_name = identifier("on_change", id(node))
        on_change_func = [ast.FunctionDef(
            name=on_change_name, args=ast.arguments(
                args=[],
                defaults=(),
            ),
            body=inner
        )]
        if node.model_name not in self._defined_models:
            raise TranslationError(
                "Cannot find bind model on current context.", node.model_name)

        body += on_change_func
        bind_attrs = ast.Tuple(
            elts=[ast.Str(s=attr) for attr in node.bind_attrs],
            ctx=ast.Load())
        bind_ons = ast.Tuple(
            elts=[ast.Str(s=attr) for attr in node.bind_ons],
            ctx=ast.Load())
        body += template("BIND_CHANGE(MODEL, BIND_ONS, BIND_ATTRS, ON_CHANGE)",
            BIND_CHANGE=Symbol(bind_change), MODEL=load(self._defined_models[node.model_name]),
            BIND_ATTRS=bind_attrs, BIND_ONS=bind_ons, ON_CHANGE=on_change_name)

        body.append(Comment("end model-binding"))
        return body
Example #17
0
    def translate(self, string, target):
        """
        >>> from chameleon.tales import test
        >>> test(PathExpr('None')) is None
        True
        """
        string = string.strip()

        if not string:
            return template("target = None", target=target)

        m = self.path_regex.match(string)
        if m is None:
            raise ExpressionError("Not a valid path-expression.", string)

        nocall, path = m.groups()

        # note that unicode paths are not allowed
        parts = str(path).split('/')

        components = []
        for part in parts[1:]:
            interpolation_args = []

            def replace(match):
                start, end = match.span()
                interpolation_args.append(
                    part[start + 1:end])
                return "%s"

            while True:
                part, count = self.interpolation_regex.subn(replace, part)
                if count == 0:
                    break

            if len(interpolation_args):
                component = template(
                    "format % args", format=ast.Str(part),
                    args=ast.Tuple(
                        list(map(load, interpolation_args)),
                        ast.Load()
                        ),
                    mode="eval")
            else:
                component = ast.Str(part)

            components.append(component)

        base = parts[0]

        if not components:
            if len(parts) == 1 and (nocall or base == 'None'):
                return template("target = base", base=base, target=target)
            else:
                components = ()

        call = template(
            "traverse(base, econtext, call, path_items)",
            traverse=self.traverser,
            base=load(base),
            call=load(str(not nocall)),
            path_items=ast.Tuple(elts=components),
            mode="eval",
            )

        return template("target = value", target=target, value=call)
Example #18
0
    def translate(self, string, target):
        """
        >>> from chameleon.tales import test
        >>> test(PathExpr('None')) is None
        True
        """
        string = string.strip()

        if not string:
            return template("target = None", target=target)

        m = self.path_regex.match(string)
        if m is None:
            raise ExpressionError("Not a valid path-expression.", string)

        nocall, path = m.groups()

        # note that unicode paths are not allowed
        parts = str(path).split('/')

        components = []
        for part in parts[1:]:
            interpolation_args = []

            def replace(match):
                start, end = match.span()
                interpolation_args.append(part[start + 1:end])
                return "%s"

            while True:
                part, count = self.interpolation_regex.subn(replace, part)
                if count == 0:
                    break

            if len(interpolation_args):
                component = template("format % args",
                                     format=ast.Str(part),
                                     args=ast.Tuple(
                                         list(map(load, interpolation_args)),
                                         ast.Load()),
                                     mode="eval")
            else:
                component = ast.Str(part)

            components.append(component)

        base = parts[0]

        if not components:
            if len(parts) == 1 and (nocall or base == 'None'):
                return template("target = base", base=base, target=target)
            else:
                components = ()

        call = template(
            "traverse(base, econtext, call, path_items)",
            traverse=self.traverser,
            base=load(base),
            call=load(str(not nocall)),
            path_items=ast.Tuple(elts=components),
            mode="eval",
        )

        return template("target = value", target=target, value=call)
Example #19
0
def store_rcontext(name):
    name = native_string(name)
    return subscript(name, load("rcontext"), ast.Store())
Example #20
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 #21
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 #22
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
Example #23
0
def store_rcontext(name):
    name = native_string(name)
    return subscript(name, load("rcontext"), ast.Store())
Example #24
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
Example #25
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 #26
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