-
Notifications
You must be signed in to change notification settings - Fork 6
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
fl
tag - Supporting lazy version of f-strings
#6
Comments
Awesome! I see you discovered that You also discovered the need for decoding "raw" strings. I'm not sure that your approach covers everything. The escapes I know of include:
|
Maybe we can similarly implement PEP 501 style "i-strings" using tag strings? |
We should be good here - nothing missed. The function So trying it out with the
The only possible gotcha here is that in regular f-strings we don't double up braces for
|
@jimbaker this is wonderful progress! |
@arcivanov @jimbaker The issue with braces in |
Raw strings are especially useful for regular expressions. I know of at least one case where tag strings would be useful for regular expressions: fixing indentation in a verbose pattern where there are interpolations that "include" subpatterns defined elsewhere. |
Could you elaborate on that example? IIUC in verbose re patterns indentation doesn't matter. So why would you need to fix it? |
I think that if our choice only "raw" or "cooked", we should go with raw. If we could come up with a clever way to say "this is a raw tagged string", then that would be ideal. But I don't see a good way of doing that. Maybe "fl-r", for a raw "fl" string? But it seems too ugly. |
Definitely too ugly. :-) I guess the |
It really helps when debugging a large pattern. If the indentation of "included" sub-patterns isn't fixed, then the resulting pattern is harder to follow when you print it out. I've had to deal with this on occasion. |
\LaTeX support 😁 - it would be nice to generate Latex with Python templates. This has been a use case for me, and likely a future one. |
Okay, raw mode seems useful enough to support. |
But wait. Aren’t curlies just as prevalent in Latex as backslashes? So what would you gain? |
There are a lot of metacharacters in Latex. But there's a difference between working with something balanced like There is this workaround for Jinja. I don't think the fact that it can be customized actually helps here: http://eosrei.net/articles/2015/11/latex-templates-python-and-jinja2-generate-pdfs |
This reminds me of something i made because I wanted f-string style templates. |
@rmorshea The need to do frame inspection (as in https://github.com/rmorshea/fstr/blob/master/fstr/fstr.py#L33) is a common requirement we see in other templating approaches where we want to avoid repeating oneself and have direct access to expressions - which is perhaps why f-strings have become so popular. See for example
(I'm sure there are many, many more examples out there!) The sharp edge here is that |
I added |
I think if I were doing this now I'd drop A reworked version of the second example might be: import fformat import fformat
common_error_message = fformat`function {function.__name__!r} failed because {error}`
def add(x, y):
try:
return x + y
except Exception as e:
msg = common_error_message.format(function=add, error=e)
print(msg)
def sub(x, y):
try:
return x + y
except Exception as e:
msg = common_error_message.format(function=sub, error=e)
print(msg)
add(1, "2")
sub("5", 3) If this seems compelling I can take a crack at implementing it. |
@rmorshea I've been thinking about the example with First, we can do something like what you propose, given that a tag string when evaluated simply returns some object, which could support a My feeling however that we might just want to wrap with a function, much like we do with some of the nested tag strings, such as with what see in the html example. So this could work:
|
Why couldn't that use f-strings? |
@gvanrossum I'm assuming - perhaps wrongly! - that @rmorshea Am I completely off with respect to the intent of the example? |
The intention was simply to have a way to use f-string syntax in a re-usable template. So to @gvanrossum's question, the answer is yes and no. Yes, you can do this with an f-string in a function as @jimbaker showed above, but no, you can't do it only with f-strings. If you were to do this just with f-string's you'd need to copy-paste the f-string and substitute in the appropriate variables (e.g. switching out def add(x, y):
try:
return x + y
except Exception as e:
msg = f`function {add.__name__!r} failed because {e}`
print(msg)
def sub(x, y):
try:
return x - y
except Exception as e:
msg = f`function {sub.__name__!r} failed because {e}`
print(msg) |
That's sort of what I figured. But if you have to write code that is essentially |
I also wrote my own implementation of lazy f-strings. It works even easier: import f
number = 33
f('{number} kittens drink milk') The actual calculation of the string followed by caching occurs at the first access. This happens transparently to the user and works, for example, for logging. My argument for why this feature should still be built into the interpreter is speed. I don't see any ways to achieve a speed comparable to the original f-strings. It is especially expensive to extract variables from closures - it cannot be done as efficiently as the interpreter does inside itself. |
@pomponchik right, it's quite possible to dynamically look up variables in a number of ways from Python's frames. One can also generalize to expressions. The numexpr package, which is part of NumPy, does something similar to your implementation; see https://github.com/pydata/numexpr/blob/master/numexpr/necompiler.py#L725 (The difference is that One difference in this proposal, besides being faster (or should be), is that the lookups of any variables in such expressions are lexically scoped. This is a well-known problem when composing with respect to nested functions, or implicitly with list comprehensions/generator expressions; see @pauleveritt's comment on a similar library jviide/htm.py#11 So that's why we need language support, or at least a transpiler, as @rmorshea as worked on in #20 |
@jimbaker My implementation takes into account lexical nesting and takes these variables too, you can check. For generator expressions, my library also works perfectly: >>> list(f('{x}') for x in range(10))
['0', '1', '2', '3', '4', '5', '6', '7', '8', '9'] In general, I don't see anything that I fundamentally couldn't implement as a library in this case. There is no way to reliably do just one thing - to make it work quickly. |
@pomponchik I tried it on a modified version of Paul's example, and it didn't blow up:
So my first reaction is, Python is maintaining enough lexical information at runtime such that it is possible to recover the lexical scoped lookup, if with rather complex (and as you note, slow running) code. I would have to see if I can find a counterexample, but your test cases you linked certainly cover the obvious one. Obviously if this is true., then it is possible to implement an arbitrary tag scheme with ordinary functions, similar to what was done in https://github.com/jviide/tagged, but respecting lexical scope. Also I'm rather impressed with your code here. It does go deep into Python's internals! |
How would you enforce that |
@gvanrossum I would do this by extracting the code object from the stack and analyzing its AST. The AST node must be a constant. So far, this improvement has not occurred to me, but in principle it looks like I could do it. But I note that it will work even longer. |
FWIW I don't want to keep discussing your |
We have a working example of |
Building on @gvanrossum's issue #1, I have implemented a lazy version of f-strings. This demo code implements the
fl
tag such that it has the same user behavior as described in python/cpython#77135Note that while
fltag.py
in the gist implements memoization on the value, it's not been optimized in any other way. (This was an imporant part of the discussion in the CPython issue above.) My assumption here is that an implementation of thefl
tag could do some memoization on the raw string decode, as well other optimizations. TBD.This can be used as follows, as seen in the
demo
function in the gist:https://gist.github.com/jimbaker/bb27803755ce890ecbcae29927cb776e
The text was updated successfully, but these errors were encountered: