Summary
I really LOVE f-strings. They're bloody awesome syntax.
For a while now I've had an idea for a little library- described below- to harness them further. A quick example of what I would like it do:
>>> import simpleformatter as sf
>>> def format_camel_case(string):
... """camel cases a sentence"""
... return ''.join(s.capitalize() for s in string.split())
...
>>> @sf.formattable(camcase=format_camel_case)
... class MyStr(str): ...
...
>>> f'{MyStr("lime cordial delicious"):camcase}'
'LimeCordialDelicious'
It would be immensely useful-- for the purposes of a simplified API, and extending usage to built-in class instances-- to find a way to hook into the builtin python formatting machinery, which would allow the custom format specification of built-ins:
>>> f'{"lime cordial delicious":camcase}'
'LimeCordialDelicious'
In other words, I'd like to override the built in format
function (which is used by the f-string syntax)-- or alternatively, extend the built-in __format__
methods of existing standard library classes-- such that I could write stuff like this:
for x, y, z in complicated_generator:
eat_string(f"x: {x:custom_spec1}, y: {x:custom_spec2}, z: {x:custom_spec3}")
I have accomplished this by creating subclasses with their own __format__
methods, but of course this will not work for built-in classes.
I could get close to it using the string.Formatter
api:
my_formatter=MyFormatter() # custom string.Formatter instance
format_str = "x: {x:custom_spec1}, y: {x:custom_spec2}, z: {x:custom_spec3}"
for x, y, z in complicated_generator:
eat_string(my_formatter.format(format_str, **locals()))
I find this to be a tad clunky, and definitely not readable compared to the f-string api.
Another thing that could be done is overriding builtins.format
:
>>> import builtins
>>> builtins.format = lambda *args, **kwargs: 'womp womp'
>>> format(1,"foo")
'womp womp'
...but this doesn't work for f-strings:
>>> f"{1:foo}"
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
ValueError: Invalid format specifier
Details
Currently my API looks something like this (somewhat simplified):
import simpleformatter as sf
@sf.formatter("this_specification")
def this_formatting_function(some_obj):
return "this formatted someobj!"
@sf.formatter("that_specification")
def that_formatting_function(some_obj):
return "that formatted someobj!"
@sf.formattable
class SomeClass: ...
After which you can write code like this:
some_obj = SomeClass()
f"{some_obj:this_specification}"
f"{some_obj:that_specification}"
I would like the api to be more like the below:
@sf.formatter("this_specification")
def this_formatting_function(some_obj):
return "this formatted someobj!"
@sf.formatter("that_specification")
def that_formatting_function(some_obj):
return "that formatted someobj!"
class SomeClass: ... # no class decorator needed
...and allow use of custom format specs on built-in classes:
x=1 # built-in type instance
f"{x:this_specification}"
f"{x:that_specification}"
But in order to do these things, we have to burrow our way into the built-in format()
function. How can I hook into that juicy f-string goodness?
from hook into the builtin python f-string format machinery
No comments:
Post a Comment