Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Using templates defined in another scope #2

Closed
jimbaker opened this issue May 1, 2022 · 6 comments
Closed

Using templates defined in another scope #2

jimbaker opened this issue May 1, 2022 · 6 comments

Comments

@jimbaker
Copy link
Owner

jimbaker commented May 1, 2022

Following up on https://peps.python.org/pep-0501/#proposal, let's demonstrate how we can create an interpolation template that can be used in another scope. We will use a helper function, template, that itself can be used as a tag:

from taglib import template

things_template = template"{num} things"

Like all tag functions, template takes Sequence[str | Thunk], but it's actually just an identity function. Let's also assume tag functions take an optional **kwargs:

def template(items: Sequence[str | Thunk], /, **kwargs) -> Sequence[str | Thunk]:
     return items

where Thunk is defined like so:

class Thunk(Protocol):
    expr: Callable  # wrapped lambda
    expr_text: str  # Python code for the expr
    format_spec: str

With this in place, we can have the following two be equivalent, assuming i18n_tag takes keyword args for variables that are not defined, in this case for num:

from i18nlib import i18n_tag as _

num = 47
print(_"{num} things")

and

from i18nlib import i18n_tag as _
from mytemplates import things_template

print(_(things_template, num=47))

Implementing this simply requires in looping over the sequence of str | Thunk, we simply do

eval(expr.__code__, {'num': 47})

so it gets num available in the namespace.

Lastly, in making the tags just be be callables, we get some very nice syntax out of it, in terms of identity for creating explicit templates, as well as being able to directly sub in the variables with a near equivalent form.

@gvanrossum
Copy link
Collaborator

Bikeshed: maybe the signature of template can be (*items: str | Thunk)? (Or if you really want keyword args, (*items: str | Thunk, **kwargs)?)

I'd also like to bikeshed the field names in Thunk, but later.

@jimbaker
Copy link
Owner Author

jimbaker commented May 1, 2022

I rather like the bikeshed on the signature - basically with tag strings, we are putting in place a new syntax for applying arguments to a function. So f(*args, **kwargs) is that most general form.

@gvanrossum
Copy link
Collaborator

Concretely, tag "foo{bar}baz" would end up calling tag("foo", bar, "baz").

What use do you have for **kwargs?

@gvanrossum
Copy link
Collaborator

gvanrossum commented May 2, 2022

@jimbaker This example in your initial comment looks wrong:

from i18nlib import i18n_tag as _
from mytemplates import things_template

print(_(things_template, num=47))

The last line (you say) is supposed to be equivalent to

num = 47
print(_"{num} things")

but the expanded example seems to be missing the "{num} things" text.

@gvanrossum
Copy link
Collaborator

FWIW I finally get this, things_template is the template created from the "{num} things" string using the template() function. Jim is demonstrating how the template can be in a different module.

@jimbaker
Copy link
Owner Author

Example is written.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants