Skip to content

Commit 9dc3312

Browse files
authored
Python 3.14: Cool New Features (WIP) (#698)
* Python 3.14: Cool New Features (initial commit) * Initial commit * Apply TR feedback
1 parent f980b2b commit 9dc3312

File tree

11 files changed

+210
-0
lines changed

11 files changed

+210
-0
lines changed

python-314/README.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
# Python 3.14: Cool New Features for You to Try
2+
3+
The materials contained in this folder are designed to complement the Real Python tutorial [Python 3.14: Cool New Features for You to Try](https://realpython.com/python314-new-features/).

python-314/fib.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
def fib(n):
2+
print(f"Calculating fib({n})...")
3+
return n if n < 2 else fib(n - 2) + fib(n - 1)

python-314/jit_status.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
try:
2+
from sys import _jit
3+
except ImportError:
4+
print("Module sys._jit unavailable")
5+
else:
6+
print("Python compiled with JIT support:", _jit.is_available())
7+
print("JIT enabled for this process:", _jit.is_enabled())
8+
print("JIT currently running:", _jit.is_active())

python-314/linked_list.py

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
# Uncomment for Python 3.14
2+
#
3+
# from dataclasses import dataclass
4+
# from typing import Any, Optional
5+
#
6+
#
7+
# @dataclass
8+
# class LinkedList:
9+
# head: Node
10+
#
11+
#
12+
# @dataclass
13+
# class Node:
14+
# value: Any
15+
# next: Optional[Node] = None

python-314/repl/cli_script.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
import argparse
2+
3+
parser = argparse.ArgumentParser("Command-Line Interface")
4+
parser.add_argument("path", help="Path to a file")
5+
parser.parse_args()

python-314/repl/pythonrc.py

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
# Set a custom color theme in Python 3.14
2+
try:
3+
from _colorize import ANSIColors, default_theme, set_theme
4+
except ImportError:
5+
pass
6+
else:
7+
custom_theme = default_theme.copy_with(
8+
syntax=default_theme.syntax.copy_with(
9+
prompt=ANSIColors.GREY,
10+
builtin="\x1b[38;2;189;147;249m",
11+
comment="\x1b[38;2;98;114;164m",
12+
definition="\x1b[38;2;139;233;253m",
13+
keyword="\x1b[38;2;255;121;198m",
14+
keyword_constant="\x1b[38;2;255;121;198m",
15+
soft_keyword="\x1b[38;2;255;121;198m",
16+
number="\x1b[38;2;189;147;249m",
17+
op="\x1b[38;2;249;152;204m",
18+
string="\x1b[38;2;241;250;140m",
19+
),
20+
traceback=default_theme.traceback.copy_with(
21+
error_highlight=ANSIColors.BOLD_YELLOW,
22+
error_range=ANSIColors.YELLOW,
23+
filename=ANSIColors.BACKGROUND_RED,
24+
frame=ANSIColors.BACKGROUND_RED,
25+
line_no=ANSIColors.BACKGROUND_RED,
26+
message=ANSIColors.RED,
27+
type=ANSIColors.BOLD_RED,
28+
),
29+
)
30+
set_theme(custom_theme)
31+
32+
# Set a custom shell prompt
33+
import platform
34+
import sys
35+
36+
version = platform.python_version()
37+
sys.ps1 = f"{version} \N{SNAKE} "
38+
sys.ps2 = "." * len(sys.ps1)

python-314/repl/test_dummy.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
import unittest
2+
3+
4+
class TestDummy(unittest.TestCase):
5+
def test_dummy(self):
6+
self.assertEqual(2 + 2, 22)

python-314/repl/todo.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
{"Shopping List": [{"title": "eggs", "details": null, "quantity": 12, "completed": true}, {"title": "bacon", "details": "smoke, 500g pack", "quantity": 1, "completed": false}, {"title": "ghee", "details": null, "quantity": 1, "completed": false}]}

python-314/subinterpreters.py

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
from concurrent.futures import (
2+
InterpreterPoolExecutor,
3+
ProcessPoolExecutor,
4+
ThreadPoolExecutor,
5+
)
6+
from time import perf_counter
7+
8+
MAX_VALUE = 35
9+
NUM_VALUES = 4
10+
NUM_WORKERS = 4
11+
12+
13+
def fib(n):
14+
return n if n < 2 else fib(n - 2) + fib(n - 1)
15+
16+
17+
def compute(Pool):
18+
t1 = perf_counter()
19+
with Pool(max_workers=NUM_WORKERS) as pool:
20+
list(pool.map(fib, [MAX_VALUE] * NUM_VALUES))
21+
t2 = perf_counter()
22+
print(f"{Pool.__name__}: {(t2 - t1):.2f}s")
23+
24+
25+
if __name__ == "__main__":
26+
compute(InterpreterPoolExecutor)
27+
compute(ProcessPoolExecutor)
28+
compute(ThreadPoolExecutor)

python-314/tstrings.py

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
from dataclasses import dataclass
2+
from string.templatelib import Interpolation, Template, convert
3+
from typing import Any
4+
5+
6+
@dataclass(frozen=True)
7+
class SQLQuery:
8+
statement: str
9+
params: list[Any]
10+
11+
def __init__(self, template: Template) -> None:
12+
items, params = [], []
13+
for item in template:
14+
match item:
15+
case str():
16+
items.append(item)
17+
case Interpolation(value, _, conversion, format_spec):
18+
converted = convert(value, conversion)
19+
if format_spec:
20+
converted = format(converted, format_spec)
21+
params.append(converted)
22+
items.append("?")
23+
super().__setattr__("statement", "".join(items))
24+
super().__setattr__("params", params)
25+
26+
27+
def find_users_query_v1(name: str) -> str:
28+
"""Return a SQL query to find users by name."""
29+
return f"SELECT * FROM users WHERE name = '{name}'"
30+
31+
32+
# Uncomment for Python 3.14:
33+
#
34+
# def find_users_query_v2(name: str) -> Template:
35+
# """Return a SQL query to find users by name."""
36+
# return t"SELECT * FROM users WHERE name = '{name}'"
37+
#
38+
#
39+
# def find_users(name: str) -> SQLQuery:
40+
# """Return a SQL query to find users by name."""
41+
# return SQLQuery(t"SELECT * FROM users WHERE name = {name}")
42+
43+
44+
def render(template: Template) -> str:
45+
return "".join(
46+
f"{text}{value}"
47+
for text, value in zip(template.strings, template.values)
48+
)
49+
50+
51+
def safer_render(template: Template) -> str:
52+
items = []
53+
for item in template:
54+
if isinstance(item, str):
55+
items.append(item)
56+
else:
57+
sanitized = str(item.value).replace("'", "''")
58+
items.append(sanitized)
59+
return "".join(items)
60+
61+
62+
if __name__ == "__main__":
63+
# Insecure f-strings
64+
print(find_users_query_v1("' OR '1'='1"))
65+
66+
# Uncomment for Python 3.14:
67+
#
68+
# # More secure t-strings
69+
# print(find_users_query_v2("' OR '1'='1"))
70+
#
71+
# # Insecure way of rendering t-strings into plain strings
72+
# print(render(find_users_query_v2("' OR '1'='1")))
73+
#
74+
# # More secure way of rendering t-strings
75+
# print(safer_render(find_users_query_v2("' OR '1'='1")))
76+
#
77+
# # Rendering t-strings into an alternative representation
78+
# print(find_users("' OR '1'='1"))

0 commit comments

Comments
 (0)