Skip to content

Commit

Permalink
✨ feature: [pep701] add f-string support and remove python3.8 (#16)
Browse files Browse the repository at this point in the history
  • Loading branch information
zrr1999 authored Mar 13, 2024
1 parent c451510 commit 8cedd5c
Show file tree
Hide file tree
Showing 9 changed files with 222 additions and 166 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/publish.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ jobs:
build:
strategy:
matrix:
target-version: [py38, py39, py310, py311, py312]
target-version: [py39, py310, py311, py312]
runs-on: ubuntu-latest
permissions:
id-token: write
Expand Down
2 changes: 1 addition & 1 deletion .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ repos:
- id: ruff-format

- repo: https://github.com/pdm-project/pdm
rev: 2.10.4
rev: 2.11.1
hooks:
- id: pdm-lock-check

Expand Down
291 changes: 132 additions & 159 deletions pdm.lock

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions pyfuture/codemod/__init__.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
from .pep604 import TransformUnionTypesCommand
from .pep622 import TransformMatchCommand
from .pep695 import TransformTypeParametersCommand
from .pep701 import TransformFStringCommand
1 change: 1 addition & 0 deletions pyfuture/codemod/pep701/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
from .fstring import TransformFStringCommand
77 changes: 77 additions & 0 deletions pyfuture/codemod/pep701/fstring.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
from __future__ import annotations

from typing import Any

import libcst as cst
from libcst import (
ClassDef,
FunctionDef,
)
from libcst.codemod import (
CodemodContext,
VisitorBasedCodemodCommand,
)
from libcst.metadata import ScopeProvider


class TransformFStringCommand(VisitorBasedCodemodCommand):
"""
Remove f-string from node, and return a new node with the formatted string.
Example:
>>> transformer = TransformFStringCommand(CodemodContext())
>>> module = cst.parse_module(\"""
... name = "world"
... x = f"hello {name}"
... y = f"hello {"world"}"
... \"""
... )
>>> new_module = transformer.transform_module(module)
>>> print(new_module.code)
name = "world"
x = "hello {:}".format(name)
y = "hello {:}".format("world")
>>> module = cst.parse_module(\"""
... result = 3.1415926
... x = f"result: {result:.2f}"
... y = f"result: {3.1415926:.2f}"
... \"""
... )
>>> new_module = transformer.transform_module(module)
>>> print(new_module.code)
result = 3.1415926
x = "result: {:.2f}".format(result)
y = "result: {:.2f}".format(3.1415926)
"""

METADATA_DEPENDENCIES = (ScopeProvider,)

def __init__(self, context: CodemodContext) -> None:
self.node_to_wrapper: dict[FunctionDef | ClassDef, Any] = {}
super().__init__(context)

def leave_FormattedString(self, original_node: cst.FormattedString, updated_node: cst.FormattedString):
expressions = []
string = ""
start = updated_node.start.strip("f")
end = updated_node.end

for node in updated_node.parts:
if isinstance(node, cst.FormattedStringExpression):
expressions.append(node.expression)
format_spec_str = ""
if (format_spec := node.format_spec) is not None:
for format_spec_node in format_spec:
assert isinstance(format_spec_node, cst.FormattedStringText), f"Unknown node type: {node}"
format_spec_str += format_spec_node.value
string += f"{{:{format_spec_str}}}"
else:
assert isinstance(node, cst.FormattedStringText), f"Unknown node type: {node}"
string += node.value
return cst.Call(
func=cst.Attribute(
value=cst.SimpleString(value=f"{start}{string}{end}"),
attr=cst.Name(value="format"),
),
args=[cst.Arg(value=expression) for expression in expressions],
)
4 changes: 4 additions & 0 deletions pyfuture/codemod/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ class RuleSet(Enum):
pep622 = "pep622"
# python 3.12+
pep695 = "pep695"
pep701 = "pep701"


def get_transformers(rule_sets: list[RuleSet] | RuleSet) -> Iterable[type[Codemod]]:
Expand All @@ -27,6 +28,7 @@ def get_transformers(rule_sets: list[RuleSet] | RuleSet) -> Iterable[type[Codemo
from .pep604 import TransformUnionTypesCommand
from .pep622 import TransformMatchCommand
from .pep695 import TransformTypeParametersCommand
from .pep701 import TransformFStringCommand

if not isinstance(rule_sets, list):
rule_sets = [rule_sets]
Expand All @@ -39,6 +41,8 @@ def get_transformers(rule_sets: list[RuleSet] | RuleSet) -> Iterable[type[Codemo
yield TransformMatchCommand
case RuleSet.pep695:
yield TransformTypeParametersCommand
case RuleSet.pep701:
yield TransformFStringCommand
case _: # pragma: no cover
raise ValueError(f"Unknown rule set: {rule_set}")

Expand Down
6 changes: 3 additions & 3 deletions pyfuture/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ def test(x: __test_T) -> __test_T:
def transfer_code(
code: str,
*,
target: tuple[int, int] = (3, 8),
target: tuple[int, int] = (3, 9),
) -> str:
"""
Transfer code to specified target version of python.
Expand All @@ -66,7 +66,7 @@ def test(x: __test_T) -> __test_T:
assert target[0] == 3, "Only support python3"
transformers = []
if target[1] < 12:
transformers.extend(get_transformers(RuleSet.pep695))
transformers.extend(get_transformers([RuleSet.pep695, RuleSet.pep701]))
if target[1] < 10:
transformers.extend(get_transformers([RuleSet.pep622, RuleSet.pep604]))
new_code = apply_transformer(
Expand All @@ -80,7 +80,7 @@ def transfer_file(
src_file: Path,
tgt_file: Path,
*,
target: tuple[int, int] = (3, 8),
target: tuple[int, int] = (3, 9),
):
"""
Transfer code from src_file and write to tgt_file.
Expand Down
4 changes: 2 additions & 2 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,12 @@ authors = [
{name = "Zhan Rongrui", email = "[email protected]"},
]
dependencies = [
"libcst>=1.1.0",
"libcst>=1.2.0",
"typer>=0.9.0",
"loguru>=0.7.2",
"rich>=13.7.0",
]
requires-python = ">=3.8"
requires-python = ">=3.9"
readme = "README.md"
license = {text = "MIT"}

Expand Down

0 comments on commit 8cedd5c

Please sign in to comment.