Skip to content

Commit 55c426b

Browse files
committed
Restructure format action, add CallerRunContextKwargs to code_action
1 parent 4c49625 commit 55c426b

28 files changed

Lines changed: 644 additions & 368 deletions

File tree

docs/reference/extensions.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ Linting and formatting via [Ruff](https://docs.astral.sh/ruff/).
1515
| Handler | Action | Description |
1616
|---|---|---|
1717
| `fine_python_ruff.RuffLintFilesHandler` | `lint_files` | Lint Python files with Ruff |
18-
| `fine_python_ruff.RuffFormatFilesHandler` | `format_files` | Format Python files with Ruff formatter |
18+
| `fine_python_ruff.RuffFormatFileHandler` | `format_python_file` | Format a single Python file with Ruff formatter |
1919

2020
**Example config:**
2121

@@ -109,13 +109,13 @@ Import sorting via [isort](https://pycqa.github.io/isort/).
109109

110110
| Handler | Action | Description |
111111
|---|---|---|
112-
| `fine_python_isort.IsortFormatFilesHandler` | `format_files` | Sort Python imports |
112+
| `fine_python_isort.IsortFormatFileHandler` | `format_python_file` | Sort Python imports in a single file |
113113

114114
**Example config** (compatible with Ruff formatter / Black):
115115

116116
```toml
117117
[[tool.finecode.action_handler]]
118-
source = "fine_python_isort.IsortFormatFilesHandler"
118+
source = "fine_python_isort.IsortFormatFileHandler"
119119
config.multi_line_output = 3
120120
config.include_trailing_comma = true
121121
config.line_length = 88
Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
1-
from .action import BlackFormatHandler, BlackFormatHandlerConfig
1+
from .format_python_file_handler import (
2+
BlackFormatFileHandler,
3+
BlackFormatFileHandlerConfig,
4+
)
25

36
__all__ = [
4-
"BlackFormatHandler",
5-
"BlackFormatHandlerConfig",
7+
"BlackFormatFileHandler",
8+
"BlackFormatFileHandlerConfig",
69
]

extensions/fine_python_black/fine_python_black/action.py renamed to extensions/fine_python_black/fine_python_black/format_python_file_handler.py

Lines changed: 55 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -2,48 +2,53 @@
22

33
import dataclasses
44
import sys
5-
6-
# import asyncio
7-
# import os
8-
from pathlib import Path
9-
10-
# from concurrent.futures import Executor # ProcessPoolExecutor,
11-
# from concurrent.futures import ThreadPoolExecutor
12-
5+
from typing import cast
136

147
if sys.version_info < (3, 12):
158
from typing_extensions import override
169
else:
1710
from typing import override
1811

1912
import black
20-
21-
# from black import WriteBack
22-
# from black.concurrency import schedule_formatting
2313
from black.mode import Mode, TargetVersion
2414

2515
from finecode_extension_api import code_action
26-
from finecode_extension_api.actions.code_quality import format_action
27-
from finecode_extension_api.interfaces import icache, ilogger, iprocessexecutor
28-
29-
30-
def get_black_mode(config: BlackFormatHandlerConfig) -> Mode:
16+
from finecode_extension_api.actions.code_quality import format_file_action
17+
from finecode_extension_api.actions.code_quality.format_python_file_action import (
18+
FormatPythonFileAction,
19+
)
20+
from finecode_extension_api.interfaces import ilogger, iprocessexecutor
21+
22+
23+
def _resolve_target_version(version: str) -> TargetVersion | None:
24+
normalized = version.upper().replace(".", "").replace("_", "")
25+
if not normalized.startswith("PY"):
26+
normalized = f"PY{normalized}"
27+
return TargetVersion.__members__.get(normalized)
28+
29+
30+
def get_black_mode(config: BlackFormatFileHandlerConfig) -> Mode:
31+
target_versions = {
32+
resolved
33+
for version in config.target_versions
34+
if (resolved := _resolve_target_version(version)) is not None
35+
}
3136
return Mode(
32-
target_versions=set([TargetVersion[ver] for ver in config.target_versions]),
37+
target_versions=target_versions,
3338
line_length=config.line_length,
3439
is_pyi=False,
3540
is_ipynb=False,
3641
skip_source_first_line=config.skip_source_first_line,
3742
string_normalization=not config.skip_string_normalization,
3843
magic_trailing_comma=not config.skip_magic_trailing_comma,
3944
preview=config.preview,
40-
python_cell_magics=set(), # set(python_cell_magics),
45+
python_cell_magics=set(config.python_cell_magics),
4146
unstable=config.unstable,
4247
)
4348

4449

4550
@dataclasses.dataclass
46-
class BlackFormatHandlerConfig(code_action.ActionHandlerConfig):
51+
class BlackFormatFileHandlerConfig(code_action.ActionHandlerConfig):
4752
# TODO: should be set
4853
target_versions: list[
4954
# TODO: investigate why list of literals doesn't work
@@ -59,69 +64,54 @@ class BlackFormatHandlerConfig(code_action.ActionHandlerConfig):
5964
skip_string_normalization: bool = False
6065
skip_source_first_line: bool = False
6166
skip_magic_trailing_comma: bool = False
62-
python_cell_magics: bool = False # it should be a set?
67+
python_cell_magics: list[str] = dataclasses.field(default_factory=list)
6368

6469

65-
class BlackFormatHandler(
66-
code_action.ActionHandler[format_action.FormatAction, BlackFormatHandlerConfig]
70+
class BlackFormatFileHandler(
71+
code_action.ActionHandler[FormatPythonFileAction, BlackFormatFileHandlerConfig]
6772
):
68-
CACHE_KEY = "BlackFormatter"
69-
7073
def __init__(
7174
self,
72-
config: BlackFormatHandlerConfig,
75+
config: BlackFormatFileHandlerConfig,
7376
logger: ilogger.ILogger,
74-
cache: icache.ICache,
7577
process_executor: iprocessexecutor.IProcessExecutor,
7678
) -> None:
7779
self.config = config
7880
self.logger = logger
79-
self.cache = cache
8081
self.process_executor = process_executor
8182

8283
self.black_mode = get_black_mode(self.config)
8384

8485
@override
8586
async def run(
8687
self,
87-
payload: format_action.FormatRunPayload,
88-
run_context: format_action.FormatRunContext,
89-
) -> format_action.FormatRunResult:
90-
result_by_file_path: dict[Path, format_action.FormatRunFileResult] = {}
91-
for file_path in payload.file_paths:
92-
file_content, file_version = run_context.file_info_by_path[file_path]
93-
try:
94-
new_file_content = await self.cache.get_file_cache(
95-
file_path, self.CACHE_KEY
96-
)
97-
result_by_file_path[file_path] = format_action.FormatRunFileResult(
98-
changed=False, code=new_file_content
99-
)
100-
continue
101-
except icache.CacheMissException:
102-
pass
103-
104-
# avoid outputting low-level logs of black, our goal is to trace finecode,
105-
# not flake8 itself
106-
self.logger.disable("fine_python_black")
107-
new_file_content, file_changed = await self.process_executor.submit(
108-
format_one, file_content, self.black_mode
109-
)
110-
self.logger.enable("fine_python_black")
111-
112-
# save for next handlers
113-
run_context.file_info_by_path[file_path] = format_action.FileInfo(
114-
new_file_content, file_version
115-
)
116-
117-
await self.cache.save_file_cache(
118-
file_path, file_version, self.CACHE_KEY, new_file_content
119-
)
120-
result_by_file_path[file_path] = format_action.FormatRunFileResult(
121-
changed=file_changed, code=new_file_content
122-
)
123-
124-
return format_action.FormatRunResult(result_by_file_path=result_by_file_path)
88+
payload: format_file_action.FormatFileRunPayload,
89+
run_context: format_file_action.FormatFileRunContext,
90+
) -> format_file_action.FormatFileRunResult:
91+
file_content = run_context.file_info.file_content
92+
file_version = run_context.file_info.file_version
93+
94+
# Avoid outputting low-level logs of black. We trace extension flow here.
95+
self.logger.disable("fine_python_black")
96+
process_result = cast(
97+
tuple[str, bool],
98+
await self.process_executor.submit(format_one, file_content, self.black_mode),
99+
)
100+
if process_result is None:
101+
raise code_action.ActionFailedException(
102+
"black formatter returned no result"
103+
) from None
104+
new_file_content = process_result[0]
105+
file_changed = process_result[1]
106+
self.logger.enable("fine_python_black")
107+
108+
# Update for next handlers in the formatting pipeline.
109+
run_context.file_info = format_file_action.FileInfo(new_file_content, file_version)
110+
111+
return format_file_action.FormatFileRunResult(
112+
changed=file_changed,
113+
code=new_file_content,
114+
)
125115

126116

127117
def format_one(file_content: str, black_mode: Mode) -> tuple[str, bool]:
Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
1-
from .action import IsortFormatFilesHandler, IsortFormatFilesHandlerConfig
1+
from .format_python_file_handler import IsortFormatFileHandler, IsortFormatFileHandlerConfig
22

33
__all__ = [
4-
"IsortFormatFilesHandler",
5-
"IsortFormatFilesHandlerConfig",
4+
"IsortFormatFileHandler",
5+
"IsortFormatFileHandlerConfig",
66
]

extensions/fine_python_isort/fine_python_isort/action.py renamed to extensions/fine_python_isort/fine_python_isort/format_python_file_handler.py

Lines changed: 21 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,15 @@
66
import isort.api as isort_api
77
import isort.settings as isort_settings
88
from finecode_extension_api import code_action
9-
from finecode_extension_api.actions.code_quality import format_files_action
10-
from finecode_extension_api.interfaces import icache, ilogger, iprocessexecutor
11-
from finecode_extension_api.resource_uri import ResourceUri
9+
from finecode_extension_api.actions.code_quality import format_file_action
10+
from finecode_extension_api.actions.code_quality.format_python_file_action import (
11+
FormatPythonFileAction,
12+
)
13+
from finecode_extension_api.interfaces import ilogger, iprocessexecutor
1214

1315

1416
@dataclasses.dataclass
15-
class IsortFormatFilesHandlerConfig(code_action.ActionHandlerConfig):
17+
class IsortFormatFileHandlerConfig(code_action.ActionHandlerConfig):
1618
profile: str | None = None
1719
line_length: int | None = None
1820
multi_line_output: int | None = None
@@ -23,47 +25,36 @@ class IsortFormatFilesHandlerConfig(code_action.ActionHandlerConfig):
2325
split_on_trailing_comma: bool | None = None
2426

2527

26-
class IsortFormatFilesHandler(
27-
code_action.ActionHandler[
28-
format_files_action.FormatFilesAction, IsortFormatFilesHandlerConfig
29-
]
28+
class IsortFormatFileHandler(
29+
code_action.ActionHandler[FormatPythonFileAction, IsortFormatFileHandlerConfig]
3030
):
3131
def __init__(
3232
self,
33-
config: IsortFormatFilesHandlerConfig,
33+
config: IsortFormatFileHandlerConfig,
3434
logger: ilogger.ILogger,
35-
cache: icache.ICache,
3635
process_executor: iprocessexecutor.IProcessExecutor,
3736
) -> None:
3837
self.config = config
3938
self.logger = logger
40-
self.cache = cache
4139
self.process_executor = process_executor
4240

4341
async def run(
4442
self,
45-
payload: format_files_action.FormatFilesRunPayload,
46-
run_context: format_files_action.FormatFilesRunContext,
47-
) -> format_files_action.FormatFilesRunResult:
48-
result_by_file_path: dict[ResourceUri, format_files_action.FormatRunFileResult] = {}
49-
for file_uri in payload.file_paths:
50-
file_content, file_version = run_context.file_info_by_path[file_uri]
43+
payload: format_file_action.FormatFileRunPayload,
44+
run_context: format_file_action.FormatFileRunContext,
45+
) -> format_file_action.FormatFileRunResult:
46+
file_content = run_context.file_info.file_content
47+
file_version = run_context.file_info.file_version
5148

52-
new_file_content, file_changed = await self.process_executor.submit(
53-
format_one, file_content, dataclasses.asdict(self.config)
54-
)
55-
56-
# save for next handlers
57-
run_context.file_info_by_path[file_uri] = format_files_action.FileInfo(
58-
new_file_content, file_version
59-
)
49+
new_file_content, file_changed = await self.process_executor.submit(
50+
format_one, file_content, dataclasses.asdict(self.config)
51+
)
6052

61-
result_by_file_path[file_uri] = format_files_action.FormatRunFileResult(
62-
changed=file_changed, code=new_file_content
63-
)
53+
# update for next handlers in the pipeline
54+
run_context.file_info = format_file_action.FileInfo(new_file_content, file_version)
6455

65-
return format_files_action.FormatFilesRunResult(
66-
result_by_file_path=result_by_file_path
56+
return format_file_action.FormatFileRunResult(
57+
changed=file_changed, code=new_file_content
6758
)
6859

6960

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
1-
from .format_files_handler import RuffFormatFilesHandler, RuffFormatFilesHandlerConfig
1+
from .format_python_file_handler import RuffFormatFileHandler, RuffFormatFileHandlerConfig
22
from .lint_files_handler import RuffLintFilesHandler, RuffLintFilesHandlerConfig
33

44
__all__ = [
5-
"RuffFormatFilesHandler",
6-
"RuffFormatFilesHandlerConfig",
5+
"RuffFormatFileHandler",
6+
"RuffFormatFileHandlerConfig",
77
"RuffLintFilesHandler",
88
"RuffLintFilesHandlerConfig",
99
]

0 commit comments

Comments
 (0)