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.00
# Future F-strings
1
2
A 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.
3
4
## Package Information
5
6
- **Package Name**: future-fstrings
7
- **Language**: Python
8
- **Installation**: `pip install future-fstrings`
9
- **Optional Dependencies**: `pip install future-fstrings[rewrite]` (for command-line tool on modern Python)
10
11
## Core Imports
12
13
```python
14
import future_fstrings
15
```
16
17
## Basic Usage
18
19
### Automatic Codec Registration (Recommended)
20
21
The package automatically registers its codec during installation via a `.pth` file, enabling f-string syntax in any file with the appropriate encoding cookie:
22
23
```python
24
# -*- coding: future_fstrings -*-
25
name = "world"
26
message = f"Hello {name}!"
27
print(message) # Outputs: Hello world!
28
```
29
30
### Manual Codec Registration
31
32
```python
33
import future_fstrings
34
future_fstrings.register()
35
36
# Now f-string files with the encoding cookie will work
37
```
38
39
### Command-line Source Transformation
40
41
Transform f-string source files for deployment to platforms that don't support f-strings:
42
43
```bash
44
future-fstrings-show input.py > output.py
45
```
46
47
Input file:
48
```python
49
# -*- coding: future_fstrings -*-
50
name = "Python"
51
print(f"Hello {name}!")
52
```
53
54
Output:
55
```python
56
# -*- coding: future_fstrings -*-
57
name = "Python"
58
print("Hello {}!".format((name)))
59
```
60
61
## Capabilities
62
63
### Codec Registration
64
65
Register the future-fstrings codec with Python's codec registry to enable automatic f-string transformation.
66
67
```python { .api }
68
def register():
69
"""
70
Register the future-fstrings codec with Python's codec registry.
71
72
This enables automatic transformation of f-strings in files that use
73
the 'future_fstrings' or 'future-fstrings' encoding declaration.
74
75
Returns:
76
None
77
"""
78
```
79
80
### Source Code Transformation
81
82
Transform Python source code containing f-strings into equivalent `.format()` calls.
83
84
```python { .api }
85
def decode(b, errors='strict'):
86
"""
87
Decode bytes containing f-string syntax into transformed source code.
88
89
Parameters:
90
b (bytes): Source code bytes to decode and transform
91
errors (str): Error handling mode ('strict', 'ignore', 'replace')
92
93
Returns:
94
tuple: (transformed_source_string, byte_length)
95
96
Raises:
97
SyntaxError: If f-string syntax is invalid
98
TokenSyntaxError: If tokenization fails with additional context
99
100
Note:
101
Requires tokenize-rt package for source transformation.
102
"""
103
104
fstring_decode = decode
105
"""
106
Alias for the decode function. Provides alternative name for accessing
107
the main f-string transformation functionality.
108
"""
109
```
110
111
### Command-line Interface
112
113
Transform and display f-string source files from the command line.
114
115
```python { .api }
116
def main(argv=None):
117
"""
118
Command-line interface for transforming f-string source files.
119
120
Reads a Python file, transforms f-strings to .format() calls,
121
and writes the result to stdout.
122
123
Parameters:
124
argv (list, optional): Command line arguments. If None, uses sys.argv
125
126
Usage:
127
future-fstrings-show filename.py
128
129
Returns:
130
None (exits with status code)
131
"""
132
```
133
134
### Platform Detection
135
136
Check if the current Python version natively supports f-strings.
137
138
```python { .api }
139
SUPPORTS_FSTRINGS: bool
140
"""
141
Boolean constant indicating whether the current Python version
142
natively supports f-string syntax (True for Python 3.6+).
143
"""
144
```
145
146
## Exception Types
147
148
```python { .api }
149
class TokenSyntaxError(SyntaxError):
150
"""
151
Specialized syntax error for f-string tokenization failures.
152
153
Provides enhanced error reporting with token context for better
154
debugging of f-string syntax issues.
155
156
Attributes:
157
e: The original exception that occurred
158
token: The token where the error was detected
159
"""
160
161
def __init__(self, e, token):
162
"""
163
Initialize TokenSyntaxError with original exception and token context.
164
165
Parameters:
166
e: Original exception
167
token: Token where error occurred
168
"""
169
```
170
171
## Codec Implementation Classes
172
173
These classes implement the Python codec interface for the future-fstrings transformation:
174
175
```python { .api }
176
class IncrementalDecoder(codecs.BufferedIncrementalDecoder):
177
"""
178
Incremental decoder implementation for the future-fstrings codec.
179
180
Handles streaming decode operations by buffering input until
181
a complete decode operation can be performed.
182
"""
183
184
def _buffer_decode(self, input, errors, final):
185
"""
186
Internal method for buffered decoding.
187
188
Parameters:
189
input (bytes): Input bytes to decode
190
errors (str): Error handling mode
191
final (bool): Whether this is the final decode call
192
193
Returns:
194
tuple: (decoded_string, bytes_consumed)
195
"""
196
197
class StreamReader(utf_8.streamreader, object):
198
"""
199
Stream reader that defers f-string decoding for better error messages.
200
201
Provides lazy decoding to maintain source position information
202
for more accurate error reporting when f-string syntax errors occur.
203
"""
204
205
@property
206
def stream(self):
207
"""Access the underlying stream with lazy decoding."""
208
209
@stream.setter
210
def stream(self, stream):
211
"""Set the underlying stream and reset decode state."""
212
```
213
214
## Version-Specific Behavior
215
216
The package behaves differently depending on Python version support for f-strings:
217
218
```python { .api }
219
# On Python 3.6+ where f-strings are natively supported:
220
# decode, IncrementalDecoder, and StreamReader are automatically
221
# reassigned to use standard UTF-8 codec components for better performance
222
223
if SUPPORTS_FSTRINGS: # Python 3.6+
224
decode = utf_8.decode
225
IncrementalDecoder = utf_8.incrementaldecoder
226
StreamReader = utf_8.streamreader
227
```
228
229
This 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.
230
231
## Advanced Usage
232
233
### Codec Information
234
235
Access codec configuration details:
236
237
```python { .api }
238
codec_map: dict
239
"""
240
Dictionary mapping codec names ('future-fstrings', 'future_fstrings')
241
to CodecInfo objects containing the complete codec implementation.
242
"""
243
```
244
245
### Error Handling
246
247
The package provides detailed error reporting for f-string syntax issues:
248
249
- **SyntaxError**: Standard Python syntax errors for malformed f-strings
250
- **TokenSyntaxError**: Enhanced errors with token context for better debugging
251
252
Common f-string syntax restrictions that trigger errors:
253
- Backslashes in expression parts: `f"bad: {'\n'}"`
254
- Comments in expressions: `f"bad: {x # comment}"`
255
- Unmatched braces: `f"bad: {x"`
256
- Nested f-strings beyond 2 levels deep
257
258
### Usage Examples
259
260
**Basic string interpolation:**
261
```python
262
# -*- coding: future_fstrings -*-
263
name = "Alice"
264
age = 30
265
print(f"Hello, {name}! You are {age} years old.")
266
```
267
268
**Expressions and formatting:**
269
```python
270
# -*- coding: future_fstrings -*-
271
import math
272
radius = 5
273
print(f"Circle area: {math.pi * radius**2:.2f}")
274
```
275
276
**Multiple f-strings:**
277
```python
278
# -*- coding: future_fstrings -*-
279
first, last = "John", "Doe"
280
email = f"{first.lower()}.{last.lower()}@example.com"
281
print(f"User: {first} {last} ({email})")
282
```