22
33import dataclasses
44import 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
147if sys .version_info < (3 , 12 ):
158 from typing_extensions import override
169else :
1710 from typing import override
1811
1912import black
20-
21- # from black import WriteBack
22- # from black.concurrency import schedule_formatting
2313from black .mode import Mode , TargetVersion
2414
2515from 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
127117def format_one (file_content : str , black_mode : Mode ) -> tuple [str , bool ]:
0 commit comments