Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

✨ feature: [pep701] add f-string support and remove python3.8 #16

Merged
merged 3 commits into from
Mar 13, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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 = "2742392377@qq.com"},
]
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
Loading