A backport of f-string syntax to Python versions < 3.6 using a custom UTF-8 compatible codec
npx @tessl/cli install tessl/pypi-future-fstrings@1.2.0A backport of Python 3.6+ f-string syntax to older Python versions (2.7, 3.4, 3.5). Works by implementing a custom UTF-8 compatible codec that performs source code transformation, converting f-string syntax into equivalent .format() calls. Includes both a runtime codec that automatically transforms code when imported and a command-line tool for pre-transforming source files.
pip install future-fstringspip install future-fstrings[rewrite] (for command-line tool on modern Python)import future_fstringsThe package automatically registers its codec during installation via a .pth file, enabling f-string syntax in any file with the appropriate encoding cookie:
# -*- coding: future_fstrings -*-
name = "world"
message = f"Hello {name}!"
print(message) # Outputs: Hello world!import future_fstrings
future_fstrings.register()
# Now f-string files with the encoding cookie will workTransform f-string source files for deployment to platforms that don't support f-strings:
future-fstrings-show input.py > output.pyInput file:
# -*- coding: future_fstrings -*-
name = "Python"
print(f"Hello {name}!")Output:
# -*- coding: future_fstrings -*-
name = "Python"
print("Hello {}!".format((name)))Register the future-fstrings codec with Python's codec registry to enable automatic f-string transformation.
def register():
"""
Register the future-fstrings codec with Python's codec registry.
This enables automatic transformation of f-strings in files that use
the 'future_fstrings' or 'future-fstrings' encoding declaration.
Returns:
None
"""Transform Python source code containing f-strings into equivalent .format() calls.
def decode(b, errors='strict'):
"""
Decode bytes containing f-string syntax into transformed source code.
Parameters:
b (bytes): Source code bytes to decode and transform
errors (str): Error handling mode ('strict', 'ignore', 'replace')
Returns:
tuple: (transformed_source_string, byte_length)
Raises:
SyntaxError: If f-string syntax is invalid
TokenSyntaxError: If tokenization fails with additional context
Note:
Requires tokenize-rt package for source transformation.
"""
fstring_decode = decode
"""
Alias for the decode function. Provides alternative name for accessing
the main f-string transformation functionality.
"""Transform and display f-string source files from the command line.
def main(argv=None):
"""
Command-line interface for transforming f-string source files.
Reads a Python file, transforms f-strings to .format() calls,
and writes the result to stdout.
Parameters:
argv (list, optional): Command line arguments. If None, uses sys.argv
Usage:
future-fstrings-show filename.py
Returns:
None (exits with status code)
"""Check if the current Python version natively supports f-strings.
SUPPORTS_FSTRINGS: bool
"""
Boolean constant indicating whether the current Python version
natively supports f-string syntax (True for Python 3.6+).
"""class TokenSyntaxError(SyntaxError):
"""
Specialized syntax error for f-string tokenization failures.
Provides enhanced error reporting with token context for better
debugging of f-string syntax issues.
Attributes:
e: The original exception that occurred
token: The token where the error was detected
"""
def __init__(self, e, token):
"""
Initialize TokenSyntaxError with original exception and token context.
Parameters:
e: Original exception
token: Token where error occurred
"""These classes implement the Python codec interface for the future-fstrings transformation:
class IncrementalDecoder(codecs.BufferedIncrementalDecoder):
"""
Incremental decoder implementation for the future-fstrings codec.
Handles streaming decode operations by buffering input until
a complete decode operation can be performed.
"""
def _buffer_decode(self, input, errors, final):
"""
Internal method for buffered decoding.
Parameters:
input (bytes): Input bytes to decode
errors (str): Error handling mode
final (bool): Whether this is the final decode call
Returns:
tuple: (decoded_string, bytes_consumed)
"""
class StreamReader(utf_8.streamreader, object):
"""
Stream reader that defers f-string decoding for better error messages.
Provides lazy decoding to maintain source position information
for more accurate error reporting when f-string syntax errors occur.
"""
@property
def stream(self):
"""Access the underlying stream with lazy decoding."""
@stream.setter
def stream(self, stream):
"""Set the underlying stream and reset decode state."""The package behaves differently depending on Python version support for f-strings:
# On Python 3.6+ where f-strings are natively supported:
# decode, IncrementalDecoder, and StreamReader are automatically
# reassigned to use standard UTF-8 codec components for better performance
if SUPPORTS_FSTRINGS: # Python 3.6+
decode = utf_8.decode
IncrementalDecoder = utf_8.incrementaldecoder
StreamReader = utf_8.streamreaderThis conditional behavior ensures optimal performance while maintaining compatibility. On older Python versions, the custom transformation classes are used, while on newer versions that natively support f-strings, the standard UTF-8 codec components are used instead.
Access codec configuration details:
codec_map: dict
"""
Dictionary mapping codec names ('future-fstrings', 'future_fstrings')
to CodecInfo objects containing the complete codec implementation.
"""The package provides detailed error reporting for f-string syntax issues:
Common f-string syntax restrictions that trigger errors:
f"bad: {'\n'}"f"bad: {x # comment}"f"bad: {x"Basic string interpolation:
# -*- coding: future_fstrings -*-
name = "Alice"
age = 30
print(f"Hello, {name}! You are {age} years old.")Expressions and formatting:
# -*- coding: future_fstrings -*-
import math
radius = 5
print(f"Circle area: {math.pi * radius**2:.2f}")Multiple f-strings:
# -*- coding: future_fstrings -*-
first, last = "John", "Doe"
email = f"{first.lower()}.{last.lower()}@example.com"
print(f"User: {first} {last} ({email})")