Skip to content

Instantly share code, notes, and snippets.

@danbradham
Last active March 7, 2017 17:55
Show Gist options
  • Save danbradham/e5f15dc9346412fedc08fc1585d3292d to your computer and use it in GitHub Desktop.
Save danbradham/e5f15dc9346412fedc08fc1585d3292d to your computer and use it in GitHub Desktop.
f-strings backport...sorta
# -*- coding: utf-8 -*-
'''
f-strings backport...sorta
==========================
What it looks like::
>>> x = 'Hello, World...'
>>> f('{x}')
'Hello, World...'
Features and Differences::
- Uses str.format instead of allowing eval of python code in {}
- Allows overriding globals and locals by passing in *args and **kwargs
- Supports python 2.7 to python 3.5
'''
import sys
from inspect import currentframe, getmro, isclass, isfunction
def f(s, *args, **kwargs):
'''
Sort of f-strings. Passes the calling frames globals and locals to
str.format instead of evaluating code in braces. Also supports positional
arguments and keyword arguments via str.format. If kwargs are passed in
they will take precedence over globals and locals.
:param args: Optional positional format args
:param kwargs: Optional format kwargs
:param _frame_: Optional kwarg - frame used to retrieve locals and globals
Usage::
>>> x = "Hello, World!"
>>> f('{x}')
'Hello, World!'
'''
frame = kwargs.pop('_frame_', currentframe().f_back)
return s.format(*args, **dict(frame.f_globals, **dict(frame.f_locals, **kwargs)))
def fdocstring(*args, **kwargs):
'''
Formats the docstrings of the decorated function or class using globals
and locals. Also supports positional arguments and keyword arguments
via str.format. If kwargs are passed in they will take precedence over
globals and locals. Runs once at execution time, so it has ZERO impact
on the performance of your class or function.
:param args: Optional positional format args
:param kwargs: Option keyword format args
:param _frame_: Optional kwarg - frame used to retrieve locals and globals
Usage::
>>> x = 'Hello from fdocstring'
>>> @fdocstring()
... def func():
... \'\'\'{x}\'\'\'
...
>>> func.__doc__
'Hello from fdocstring'
'''
def do_fdocstring(obj):
frame = kwargs.pop('_frame_', currentframe().f_back)
fkwargs = dict(frame.f_globals, **frame.f_locals)
if isfunction(obj):
obj.__doc__ = obj.__doc__.format(*args, **dict(fkwargs, **kwargs))
if isclass(obj):
bases = getmro(obj)
for cls in bases[::-1]:
fkwargs.update(cls.__dict__)
fkwargs.update(**kwargs)
attrs = dict(obj.__dict__)
attrs['__doc__'] = obj.__doc__.format(*args, **fkwargs)
for k, v in attrs.items():
if isfunction(v):
v.__doc__ = v.__doc__.format(*args, **fkwargs)
obj = type(obj.__name__, tuple(bases[1:]), attrs)
return obj
return do_fdocstring
def printf(s, *args, **kwargs):
'''
Write `f(s, *args, **kwargs)` to sys.stdout
:param _stdout_: Optional kwarg - Override output stream
Usage::
>>> x = 'Hello from printf'
>>> printf('{x}')
Hello from printf
'''
kwargs['_frame_'] = currentframe().f_back
stdout = kwargs.pop('_stdout_', sys.stdout)
stdout.write(f(s + '\n', *args, **kwargs))
def test():
import doctest
sys.stdout.write('Running Doctests...............')
doctest.testmod()
sys.stdout.write('OK!\n')
sys.stdout.write('Running Tests..................')
# For testing purposes we manually assign to globals
# These are equivalent to module level global variables
globals()['URL'] = '/api/v1.0'
globals()['NUM'] = 10
class OBJ: VAL = 20
globals()['OBJ'] = OBJ
globals()['FS'] = f('{URL}/{NUM:0>3}/{OBJ.VAL}')
@fdocstring()
def test_function():
'''{URL}/{NUM:0>3}/{OBJ.VAL}'''
@fdocstring()
class TestClass(object):
'''{URL}'''
URL = '/api/v1.1'
def test_method(self):
'''{URL}/method'''
def no_tokens(self):
'''No Tokens'''
@fdocstring()
class TestSubclassInherit(TestClass):
'''{URL}'''
def test_method(self):
'''{URL}/subclass_method'''
def test_method2(self):
'''{FS}'''
return f('{FS}/subclass_method')
@fdocstring(1, 2, URL='/api/v1.2', FS='WHAT')
class TestSubclassOverride(TestClass):
'''{} {} {URL}'''
def test_method(self):
'''{URL}/subclass_method'''
def test_method2(self):
'''{FS}'''
return f('{FS}/subclass_method')
def test_function2():
a = 1
b = 2
c = 'funcy fresh'
return f('{a}, {b}, {c}')
def test_function3():
a = 1
b = 2
c = 'funcy fresh'
return f('{a}, {b}, {c}', c='funky fresh')
assert FS == '/api/v1.0/010/20'
assert test_function.__doc__ == '/api/v1.0/010/20'
assert TestClass.__doc__ == '/api/v1.1'
assert TestClass.test_method.__doc__ == '/api/v1.1/method'
assert TestClass.no_tokens.__doc__ == 'No Tokens'
assert TestSubclassInherit.__doc__ == '/api/v1.1'
assert TestSubclassInherit.test_method.__doc__ == '/api/v1.1/subclass_method'
assert TestSubclassInherit.test_method2.__doc__ == '/api/v1.0/010/20'
assert TestSubclassInherit().test_method2() == '/api/v1.0/010/20/subclass_method'
assert TestSubclassOverride.__doc__ == '1 2 /api/v1.2'
assert TestSubclassOverride.test_method.__doc__ == '/api/v1.2/subclass_method'
assert TestSubclassOverride.test_method2.__doc__ == 'WHAT'
assert TestSubclassOverride().test_method2() == '/api/v1.0/010/20/subclass_method'
assert test_function2() == '1, 2, funcy fresh'
assert test_function3() == '1, 2, funky fresh'
sys.stdout.write('OK!\n')
sys.stdout.write('Running Benchmark..............')
from timeit import timeit
t = timeit(
"f('{a} {b} {c}')",
setup='from __main__ import f; a = 1; b = 2; c = 3',
number=100000
)
sys.stdout.write('OK!\n')
printf("f('{{a}} {{b}} {{c}}') x 100000 took {}", t)
if __name__ == '__main__':
if sys.argv[1] == 'test':
test()
@danbradham
Copy link
Author

f-strings backport...sorta

What it looks like:

>>> x = 'Hello, World...'
>>> f('{x}')
'Hello, World...'

Features:

  • Uses str.format instead of allowing eval of python code in {}
  • Allows overriding globals and locals by passing in *args and **kwargs
  • Supports python 2.7 to python 3.5

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment